我的table1已经过时,正在被table2取代。我的目标是将table1中的所有记录移动到table2。为了避免在移动数据时发生id冲突,我运行了这个函数,如果匹配的table2.id已经存在,它只会更新table1.ids:
DO
$do$
DECLARE
row RECORD;
BEGIN
FOR row IN
SELECT t1.id, t1.name, t2.id, t2.name
FROM sch.table1 AS t1
JOIN sch.table2 AS t2
ON t1.id = t2.id
LOOP
UPDATE sch.table1
SET id = (select greatest(
(select max(id) from sch.table1),
(select max(id) from sch.table2)) + 1)
WHERE id = row.id;
-- more code here that updates tables with table1.id as foreign key
END LOOP;
END
$do$;
在大多数情况下,该功能按预期工作,正确地整理了100多条记录。但奇怪的是,似乎它只是一点点地异步运行。在函数运行之前,我在table1中有这8条记录(我已经编辑了名称):
id |name
----|-----------------------------
450 |Number Zero
451 |Number One
452 |Number Two
453 |Number Three
454 |Number Four
455 |Number Five
456 |Number Six
457 |Number Seven
在函数运行后,这8条记录是:
id |name
----|-----------------------------
1138|Number Zero
1139|Number One
1139|Number Two
1140|Number Three
1140|Number Four
1141|Number Five
1141|Number Six
1142|Number Seven
“第一号”之前和“第六号”之后的每条记录都相应更新。奇怪的是6条记录配对了。两个记录分配了id 1139,1140和1141.我不知道这是怎么发生的,我需要知道才能找到避免这种情况的方法!
有什么想法吗?
PS。我在DataGrip上运行SQL代码,PostgreSQL DB位于AWS上。只是说明这一点,以防任何一个是重要的。
答案 0 :(得分:1)
您正在从正在更新的表中获取,并且获取(连接)基于您正在更新的列,这可能是主键。 Postgres在进入循环之前不得选择所有行。
在您的代码中还有一些其他问题:
row.id
的价值应该是什么; t1.id
或t2.id
?您应该为id
和name
列使用显式别名。ORDER BY
子句。如果线条按你想要的那样编号,那就巧合了。id
的{{1}}的最大值是循环的不变量。因此,您可以将循环外部的最大值计算到变量中,并在循环内增加此变量的值。t2
的值。所以你可以省略加入。总而言之,你应该尝试类似的事情:
table2
但总是存在更新DO
$do$
DECLARE
row RECORD;
i INTEGER;
BEGIN
i := greatest(
(select max(id) from sch.table1),
(select max(id) from sch.table2)) + 1;
FOR row IN
SELECT t1.id, t1.name FROM sch.table1 AS t1 ORDER BY ...
LOOP
UPDATE sch.table1 SET id = i WHERE id = row.id;
i := i + 1;
END LOOP;
END
$do$;
主键的问题。如果此代码不起作用,则应将主键保存在临时列中:
table1