在PostgreSQL中交换具有UNIQUE约束的列的记录值

时间:2016-05-27 21:53:54

标签: sql database postgresql

目标:

对于具有 UNIQUE约束的特定列,某些表的记录中的

交换值。

实施例

考虑下表:

CREATE TABLE records (id numeric, name text);
ALTER TABLE records ADD CONSTRAINT uniq UNIQUE (id);
INSERT INTO records VALUES
  (1, 'First record'),
  (3, 'Second record'),
  (2, 'Third record'),
  (4, 'Fourth record');

我们从查询SELECT id, name FROM records ORDER BY id;

获得
| id |          name |
|----|---------------|
|  1 |  First record |
|  2 |  Third record |
|  3 | Second record |
|  4 | Fourth record |

idSecond record 已交换需要Third record秒。也就是说,我想要以下输出:

| id |          name |
|----|---------------|
|  1 |  First record |
|  2 | Second record | <-- this record previously had the id 3
|  3 |  Third record | <-- this record previously had the id 2
|  4 | Fourth record |

显然:

  • 我不能只为这些记录交换name,我的实际数据库比这个大一点:
    • 此表存储了更多信息
    • id列实际上是其他表引用的PRIMARY KEY
  • 我不想轻易使用尚未分配给任何记录的临时id来执行此交换。
  • 有问题的'交换'不仅涉及两个记录,还包含任意长记录。
  • 这些记录在技术上也不会'交换',而只是重新分配,具有以下属性:
    • 每次批量重新分配操作后,每个id始终都是唯一的
    • 以前作业中的大多数id与新作业地图重叠。

我尝试了什么:

我尝试通过生成临时重新分配表并在UPDATE ... SET ... FROM ... WHERE查询中使用它来在单个查询中交换我的数据。

这是我的临时重新分配表,它将交换id s 23

(VALUES
  (3, 2),
  (2, 3)
) AS swap(id, new_id)

以下是我如何使用它:

UPDATE records AS record SET
  id = swap.new_id
FROM (VALUES
  (3, 2),
  (2, 3)
) AS swap(id, new_id)
WHERE record.id = swap.id;

SELECT id, name FROM records ORDER BY id;

正如您在SQLFiddle link中看到的那样,它很有效......直到您在UNIQUE列上添加id约束。

我怎样才能在上面列出的所有条件下工作?

1 个答案:

答案 0 :(得分:7)

问题在于CONSTRAINT的定义。

PosgreSQL &#39; CONSTRAINTS可以是DEFERRABLE。从their documentation开始,我们收集以下内容:

  

在每个语句的末尾检查IMMEDIATE约束。在事务提交之前不会检查DEFERRED约束。每个约束都有自己的IMMEDIATE或DEFERRED模式。

在我们面临的情况下,id仍然是UNIQUE s AFTER 交易,而CONSTRAINT确实不能满意 DURING 重新分配id s。

只需制作相关的CONSTRAINT DEFERRABLE即可解决手头的问题。

CREATE TABLE records (id numeric, name text);
ALTER TABLE records ADD CONSTRAINT uniq UNIQUE (id) DEFERRABLE INITIALLY IMMEDIATE;
INSERT INTO records VALUES --                           ^
  (1, 'First record'),     --                          HERE
  (3, 'Second record'),
  (2, 'Third record'),
  (4, 'Fourth record');