【问题标题】:For each selected row insert another?对于每个选定的行插入另一个?
【发布时间】:2019-08-05 21:25:11
【问题描述】:

我有一个表 TableA。它有列idtyperelatedIdanother1another2。列type 可以有值1, 2 or 3。 我需要的是,对于 TableA 中的每一行,其中type = 1,在同一个表中插入另一行,并用新插入行的id 更新原始行(列relatedId)。此外,新插入行中某些列的值应从原始行中复制。

所以对于当前状态:

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

【问题讨论】:

    标签: sql postgresql


    【解决方案1】:

    假设文本是唯一的,您可以这样做:

    demo:db<>fiddle

    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-ids 也存储在新的 type3-columns 中。如果这对你来说没问题,你可以这样做:

    demo:db<>fiddle

    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-ids 存储在新的相关 ID 列中。因此,在每种情况下,都可以通过该值找到原始 id。

    不幸的是,您不能在另一个 WITH 子句中将这些列设为 NULL,因为 WITH 子句仅适用于现有数据。此时查询本身尚未完成。所以新记录在物理上并不存在。


    这个可以用……

    demo:db<>fiddle

    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 被取走。 row_number() 窗口函数将连续的行数添加到记录中。因此,如果两个数据集具有相同的顺序,则旧 id 应该获得与其对应的新 id 相同的行号。在这种情况下,识别是可能的。对于这个工作的小例子......

    --> 编辑:这似乎是在说:是的,从 Postgres 9.6 开始,订单将被保留https://stackoverflow.com/a/50822258/3984221

    【讨论】:

    • 添加了另一个查询
    • @testuser:这有帮助吗?请不要忘记接受正确答案,否则请告诉我们要更改的内容
    【解决方案2】:

    根据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 中,我们得到所有行,这些行应该连同它们的 row_number() 按 ID 排序一起插入。在第二个中,我们通过从第一个 CTE 中选择按 ID 显式排序来插入它们。我们在第二个 CTE 中返回插入的 ID,以便我们可以在第三个 CTE 中选择它,在这里我们再次添加一个按 ID 排序的row_number()。我们现在可以通过行号连接第一个和第三个 CTE,以获得原始 ID 和新插入的 ID 对。在此基础上,我们可以更新设置相关 ID 的表。

    db<>fiddle

    【讨论】:

    猜你喜欢
    • 2016-06-30
    • 1970-01-01
    • 1970-01-01
    • 2022-01-24
    • 1970-01-01
    • 2014-05-21
    • 1970-01-01
    • 2020-01-25
    相关资源
    最近更新 更多