是否可以在 PostgreSQL 中使用单个语句更新插入到自引用表中?

Zac*_*ith 5 postgresql upsert self-join

如果我有一张桌子A,就像这样:

A {
 id SERIAL
 title TEXT
 ...
 parentId INT references A.id via a foreign key constraint
}
Run Code Online (Sandbox Code Playgroud)

我正在从源表中提取数据 - A_SOURCE- 没有parentId列的地方。取而代之的是一parentTitle列。所以源表看起来像这样:

A_SOURCE {
  title TEXT
  parentTitle TEXT
}
Run Code Online (Sandbox Code Playgroud)

我开始编写一个 upsert 语句以A通过从表中选择插入到表中,A_SOURCE然后才意识到我无法轻松地parentTitle将源中的列解析parentId为目标中的 a。

由于我无法确定在处理子项时是否已插入父项,因此连接或子查询可能不会返回任何结果。

我的 upsert 语句看起来像这样:

with source as (
  select
   title
   parentTitle
  from A_SOURCE 
)

insert into A
select
title
... I don't think I can resolve to parentId here?
from source
on concflict ...;
Run Code Online (Sandbox Code Playgroud)

我知道我可以运行两个单独的语句:

  1. 插入null作为 parentId
  2. 然后更新第二条语句中的 parentId 字段

但是有可能在单个查询中做到这一点吗?

Lau*_*lbe 3

准备要使用递归 CTE 插入的值,该递归 CTE 预先计算 sid并定义顺序。然后按顺序插入:

WITH RECURSIVE a_sort AS (
      /* get all entries without a parent (rank 1) */
      SELECT nextval('a_id_seq')::integer AS id,
             title,
             NULL::integer AS parentid,
             parenttitle,
             1 AS rank
      FROM a_source
      WHERE parenttitle IS NULL
   UNION ALL
      /* recursively get all the immediate children and increase rank */
      SELECT nextval('a_id_seq')::integer,
             src.title,
             a_sort.id,
             a_sort.title,
             a_sort.rank + 1
      FROM a_source AS src
         JOIN a_sort ON a_sort.title = src.parenttitle
)
INSERT INTO a
SELECT id,
       title,
       parentid
FROM a_sort
ORDER BY rank;
Run Code Online (Sandbox Code Playgroud)

该解决方案的优点在于,它使用了后面的序列a.ida_id_seq在示例中),因此在完成后序列会自动具有正确的值。

该解决方案假设输入的数据a_source是正确的,即不包含循环。