Ped*_*ges 7 postgresql postgresql-11
我似乎无法找到一种方法来实现这一点(或不)
我需要实现的是非标准的,因此我很难找到解决方案。
我需要编写一个数据迁移工具来将旧记录与表中的新记录“交换”,但我有以下要求/约束:
所以我的理想解决方案是,蛮力交换他们的主键......
问题是如何在 postgresql 中的两条记录之间交换主键。我很难找到一种不会因重复键异常而失败的方法,即一种以事务方式运行更新“验证”的方法。
我试过了
UPDATE table
SET id = (CASE id WHEN 1 THEN 2 WHEN 2 THEN 1 ELSE id END)
Run Code Online (Sandbox Code Playgroud)
UPDATE table
SET id = CASE id WHEN 1 THEN 2 WHEN 2 THEN 1 END
WHERE id IN (1, 2);
Run Code Online (Sandbox Code Playgroud)
都在重复键约束上失败
我正在使用 PostgreSQL 11.6
数字主键的批量更新解决方案,感谢以下好人的输入:
-- disable foreign key constraint validation
BEGIN;
SET session_replication_role='replica';
-- update the pairs of ids to their negative counterparts
WITH query AS ('query to get pairs of ids to swap')
UPDATE table
SET id = -id
WHERE id in (query.id1, query.id2);
-- update the pairs of negated ids to their positive counterparts swapped
WITH query AS ('query to get pairs of ids to swap')
UPDATE table
SET id = CASE id WHEN query.id1 THEN -query.id2
WHEN query.id2 THEN -query.id1
END
WHERE id in (query.id1, query.id2);
-- enable foreign key constraint validation
SET session_replication_role='original';
COMMIT;
Run Code Online (Sandbox Code Playgroud)
我知道这是可能的唯一方法是使用延迟约束。
您需要删除主键,
alter table x drop constraint x_pkey;
Run Code Online (Sandbox Code Playgroud)
并将其再次添加为可推迟:
alter table x add primary key (id) deferrable initially immediate;
Run Code Online (Sandbox Code Playgroud)
在执行更新之前,您可以推迟约束:
set constraints x_pkey deferred;
Run Code Online (Sandbox Code Playgroud)
然后执行更新 和commit
。
https://dbfiddle.uk/?rdbms=postgres_12&fiddle=469e5168c84bdbb8361426d4458bcf88
当然,如果您有其他表引用该表,您也需要删除并重新创建外键。
您可以使用延迟约束。为此,您需要删除并重新创建主键:
CREATE UNIQUE INDEX mytable_primkey ON mytable (id);
ALTER TABLE mytable DROP CONSTRAINT mytable_pkey;
ALTER TABLE mytable ADD PRIMARY KEY USING INDEX mytable_primkey
DEFERRABLE INITIALLY DEFERRED;
Run Code Online (Sandbox Code Playgroud)
更新本身可以这样完成:
UPDATE mytable SET id = 3 - id WHERE id IN (1, 2);
Run Code Online (Sandbox Code Playgroud)
这里以 1 和 2 为例,你可以用任何数字来做。
如果您负担不起添加延迟主键所需的停机时间,您可以再进行一次这样的更新;
BEGIN;
UPDATE mytable SET id = 0 WHERE id = 1;
UPDATE mytable SET id = 1 WHERE id = 2;
UPDATE mytable SET id = 2 WHERE id = 0;
COMMIT;
Run Code Online (Sandbox Code Playgroud)
这里 0 是一个任意值,不用作 的值id
。
小智 2
感谢其他答案作者的输入,批量更新数字主键的解决方案:
-- disable foreign key constraint validation
BEGIN;
SET session_replication_role='replica';
-- update the pairs of ids to their negative counterparts
WITH query AS ('query to get pairs of ids to swap')
UPDATE table
SET id = -id
WHERE id in (query.id1, query.id2);
-- update the pairs of negated ids to their positive counterparts swapped
WITH query AS ('query to get pairs of ids to swap')
UPDATE table
SET id = CASE id WHEN query.id1 THEN -query.id2
WHEN query.id2 THEN -query.id1
END
WHERE id in (query.id1, query.id2);
-- enable foreign key constraint validation
SET session_replication_role='origin';
COMMIT;
Run Code Online (Sandbox Code Playgroud)