我只有一个表TableA。它具有列id
,type
,relatedId
,another1
,another2
列。列type
可以具有值1, 2 or 3
。
我需要的是,对于TableA中的每一行,type = 1
,在同一表中插入另一行,并用新插入的行的relatedId
更新原始行(列id
)。另外,应从原始行中复制新插入的行中某些列的值。
所以对于当前状态:
id|type|relatedId|another1
10| 1 |null|"some text"
11| 2 |null|"somthing"
12| 1 |null|"somthing else"
结果应为:
id|type|relatedId|another1
10| 1 |13 |"some text" - now has relationship to 13
11| 2 |null|"somthing"
12| 1 |14 |"somthing else" - now has relationship to 13
13| 3 |null|"some text" - inserted, "another1" is copied from 10
14| 3 |null|"somthing else" - inserted, "another1" is copied from 12
答案 0 :(得分:0)
假设文本是唯一的,您可以这样做:
WITH ins AS (
INSERT INTO tablea(type, related_id, another1)
SELECT 3, null, another1
FROM tablea
WHERE type = 1
RETURNING id, another1
)
UPDATE tablea t
SET related_id = s.id
FROM (
SELECT * FROM ins
) s
WHERE s.another1 = t.another1 AND t.type = 1
WITH子句允许顺序执行两个单独的语句。因此,首先插入新数据。使用新生成的ID,您可以随后更新旧数据。因为必须匹配原始数据,所以文本作为标识符很有用。
仅在不必使用(1,'something')的数据集时才有效。这样一来,很难确定两个记录中的哪个是每个副本的原始记录。
另一种方法也可以将type1-id存储在新的type3列中。如果您认为可以,则可以执行以下操作:
WITH ins AS (
INSERT INTO tablea(type, related_id, another1)
SELECT 3, id, another1
FROM tablea
WHERE type = 1
RETURNING id, related_id, another1
)
UPDATE tablea t
SET related_id = s.id
FROM (
SELECT * FROM ins
) s
WHERE s.related_id = t.id
这会将原始的type1-id存储在新的type1-id中。因此,在每种情况下都可以在该值上找到原始ID。
不幸的是,您不能在另一个WITH子句中使这些列为NULL,因为WITH子句仅适用于现有数据。目前,查询本身尚未完成。因此,新记录实际上并不存在。
这可以工作...
WITH to_be_copied AS (
SELECT id, another1
FROM tablea
WHERE type = 1
), ins AS (
INSERT INTO tablea(type, related_id, another1)
SELECT 3, null, another1
FROM to_be_copied
ORDER BY id -- 1
RETURNING id, another1
)
UPDATE tablea t
SET related_id = s.type3_id
FROM (
SELECT
*
FROM
(SELECT id as type1_id, row_number() OVER (ORDER BY id) FROM to_be_copied) tbc
JOIN
(SELECT id as type3_id, row_number() OVER (ORDER BY id) FROM ins) i
ON tbc.row_number = i.row_number
) s
WHERE t.id = s.type1_id
此解决方案假定(1)中的给定顺序确保了新记录的插入顺序。实际上,我对此不太确定。但是,如果这样:首先查询所有type1记录。之后,将进行复制(以相同的顺序!)。之后,将使用旧记录ID和新记录ID。 row_number()窗口函数将连续的行计数添加到记录。因此,如果两个数据集具有相同的顺序,则旧ID应获得与其对应的新ID相同的行号。在这种情况下,可以进行识别。对于这个小例子,这行得通...
-> 编辑:这似乎是在说:是的,自Postgres 9.6 https://stackoverflow.com/a/50822258/3984221
起,订单将被保留。答案 1 :(得分:0)
根据this question,从9.6开始,Postgres保留通过SELECT
用显式ORDER BY
插入的行的顺序。我们可以使用它来将插入的行与使用row_number()
来自的行进行连接。
WITH
"cte1"
AS
(
SELECT "id",
3 "type",
"related_id",
"another1",
row_number() OVER (ORDER BY "id") "rn"
FROM "tablea"
WHERE "type" = 1
),
"cte2"
AS
(
INSERT INTO "tablea"
("type",
"another1")
SELECT "type",
"another1"
FROM "cte1"
ORDER BY "id"
RETURNING "id"
),
"cte3"
AS
(
SELECT "id",
row_number() OVER (ORDER BY "id") "rn"
FROM "cte2"
)
UPDATE "tablea"
SET "related_id" = "cte3"."id"
FROM "cte1"
INNER JOIN "cte3"
ON "cte3"."rn" = "cte1"."rn"
WHERE "cte1"."id" = "tablea"."id";
在第一个CTE中,我们获得了所有行,这些行应与ID排序的row_number()
一起插入。在第二个中,我们通过从第一个CTE中选择按ID显式排序来插入它们。我们在第二个CTE中返回插入的ID,以便我们在第三个CTE中选择它,然后再次添加ID排序的row_number()
。现在,我们可以通过行号加入第一个和第三个CTE,以获得成对的原始ID和新插入的ID。在此基础上,我们可以更新设置相关ID的表。