PostgreSQL-根据从另一个表中选择插入行,并使用新插入行在表中更新FK
技术问答
234 人阅读
|
0 人回复
|
2023-09-14
|
我在两个表之间迁移数据(拆分相关表)。现有表为reminders,它具有一start列和一个dateset_id指向新dateset该表还有一个新添加的列start列。每一行reminders,我要INSERT一个新的行dateset与start和UPDATE在相应的行reminders与新插入的datesetID。
9 I2 q2 R ^8 b3 [这是我试过的SQL:
- n$ l8 N A! s/ W" r$ YWITH inserted_datesets AS ( INSERT INTO dateset (start) SELECT start FROM reminder RETURNING reminder.id AS reminder_id,id AS dateset_id)UPDATE reminderSET dateset_id = ids.dateset_idFROM inserted_datesets AS idsWHERE reminder.id = ids.reminder_id我收到了一个错误missing FROM-clause entry for table"reminder",因为我reminder.id在该RETURNING该列包含在句子中,但实际上没有选择插入。这是合理的,但我不知道如何修改查询以执行所需的操作。我缺乏完全不同的方法吗?
" h. b4 K w. Q5 J' }
7 \# ` b( K/ y 解决方案:
2 M, ?6 y2 S- s: p) L$ I# Z 解决问题的方法有几种。2 P- k5 b; N# I0 ] d. O
1.临时添加一列
A% k8 O) e/ y" _# Q4 R* Q& e正如其他人所说,一个简单的方法是临时添加列reminder_id到中dateset。原装填充它IDs从reminder表。使用它reminder与dateset表连接。删除临时列。
# T. A! ]' Z8 m2.何时启动是唯一的, H. Z* z: q' X) h H* }0 Y5 M
如果该start列的值是唯一的,可以通过remindertable与该dateset列表连接,无需额外连接start列。
6 t: g6 R! ^8 dINSERT INTO dateset (start)SELECT start FROM reminder;WITHCTE_JoinedAS( SELECT reminder.id AS reminder_id reminder.dateset_id AS old_dateset_id dateset.id AS new_dateset_id FROM reminder INNER JOIN dateset ON dateset.start = reminder.start)UPDATE CTE_JoinedSET old_dateset_id = new_dateset_id;3.启动时不是唯一的
2 H+ c8 m5 y* l- u2 }1 O即使在这种情况下,也可以在没有临时列的情况下操作。主要思路如下。让我们来看看这个例子:
- M2 a# d9 [: O$ L# {# q9 J我们有两个reminder具有相同start值和ID 3和7的行:
" r6 P+ q$ J. T k2 E2 o7 ~reminderid start dateset_id3 2015-01-01 NULL7 2015-01-01 NULL插入后dateset,将生成新的ID,例如1和2:8 e9 s" ~/ w9 C+ o
datesetid start1 2015-01-012 2015-01-01如何链接这两行并不重要。最终的结果可能是
6 l! J5 Y5 i& m% Oreminderid start dateset_id3 2015-01-2015年17 -01-01 2或者
o( {* L& s( V. wreminderid start dateset_id3 2015-01-2015年1501 27 -01-01 1这两个变体都是正确的。这给我们带来了以下解决方案。3 g% v$ K4 Y- R6 a
只需先插入所有行。
5 M; |+ C$ m6 f. {INSERT INTO dateset (start)SELECT start FROM reminder;在start知道列不是唯一匹配/连接列上的两个表。ROW_NUMBER连接两列让它独一无二7 g2 W4 I2 e- |$ M/ t, p4 |3 H s
。查询可以缩短,但我清楚地解释了每一步:
: x( S+ l! B4 \& ]; FCTE_dateset_rn.id AS new_dateset_id FROM CTE_reminder_rn INNER JOIN CTE_dateset_rn ON CTE_dateset_rn.start = CTE_reminder_rn.start AND CTE_dateset_rn.rn = CTE_reminder_rn.rn)UPDATE CTE_JoinedSET old_dateset_id = new_dateset_id;CTE_dateset_rn.id AS new_dateset_id FROM CTE_reminder_rn INNER JOIN CTE_dateset_rn ON CTE_dateset_rn.start = CTE_reminder_rn.start AND CTE_dateset_rn.rn = CTE_reminder_rn.rn)UPDATE CTE_JoinedSET old_dateset_id = new_dateset_id;我希望你能从代码中清楚地知道它的功能,特别是当你比较它没有的简单版本时ROW_NUMBER。显然,即使start是唯一的,复杂的解决方案也可以使用,但效率却不如简单的解决方案。% [! g2 b& W# R1 _& Z8 x
假设这个解决方案dateset在这个过程之前是空的。 |
|
|
|
|
|