INSERT INTO ... FROM SELECT ... RETURNING id 映射

INSERT INTO ... FROM SELECT ... RETURNING id mappings

我正在使用 PostgreSQL 9.3.

我想复制一些数据库记录。由于我为 table 使用自动递增 pk id,我想取回从重复记录生成的 id 到原始记录的 id 映射。例如,假设我有一个 table posts 其中有 2 条记录:

 [{'id': 1, 'title': 'first'}
, {'id': 2. 'title': 'second'}]

与SQL:

INSERT INTO posts (title) SELECT title FROM posts RETURNING id, ??

我希望看到如下映射:

 [{'id': 3, 'from_id': 1}
, {'id': 4, 'from_id': 2}]

知道如何填写上面的问号使其生效吗?非常感谢!

这对于 UPDATE 来说会更简单,其中加入更新的其他行对 RETURNING 子句可见:

  • Return pre-UPDATE column values using SQL only

目前 INSERT 不可能。 The manual:

The expression can use any column names of the table named by table_name

table_nameINSERT 命令的目标。

您可以使用 (data-modifying) CTEs 使其正常工作。
假设 title 每个查询 唯一 ,否则你需要做更多:

WITH sel AS (
   SELECT id, title
   FROM   posts
   WHERE  id IN (1,2)   -- select rows to copy
   )
, ins AS (
   INSERT INTO posts (title)
   SELECT title FROM sel
   RETURNING id, title
 )
SELECT ins.id, sel.id AS from_id
FROM   ins
JOIN   sel USING (title);

如果 title 不是每个查询唯一的(但至少 id 每个 table 是唯一的):

WITH sel AS (
   SELECT id, title, row_number() OVER (ORDER BY id) AS rn
   FROM   posts
   WHERE  id IN (1,2)   -- select rows to copy
   ORDER  BY id
   )
, ins AS (
   INSERT INTO posts (title)
   SELECT title FROM sel ORDER  BY id  -- ORDER redundant to be sure
   RETURNING id
 )
SELECT i.id, s.id AS from_id
FROM  (SELECT id, row_number() OVER (ORDER BY id) AS rn FROM ins) i
JOIN   sel s USING (rn);

第二个查询依赖于未记录的实现细节,即按提供的顺序插入行。它适用于所有当前版本的 Postgres,并且可能不会崩溃。

db<>fiddle here
sqlfiddle

恕我直言,最简单的解决方案是在 table 中添加一列,您可以在其中放置已克隆行的 ID。

如果 postsid 列是 serial 类型,它的生成方式类似于 nextval('posts_id_seq'::regclass), 您可以为每个新行手动调用此函数

with
sel as (
  SELECT id, title, nextval('posts_id_seq'::regclass) new_id
  FROM   posts
  WHERE  id IN (1,2)
),
ins as (
  INSERT INTO posts (id, title)
  SELECT new_id, title
  FROM sel
)
SELECT id, new_id
FROM sel

它适用于任何数据,包括非唯一数据title