【问题标题】:INSERT INTO ... FROM SELECT ... RETURNING id mappingsINSERT INTO ... FROM SELECT ... 返回 id 映射
【发布时间】:2015-03-25 13:21:42
【问题描述】:

我使用的是 PostgreSQL 9.3。

我想复制一些数据库记录。由于我对表使用了自动增量 pk id,因此我想从生成的重复记录的 id 到原始记录的 id 中取回 id 映射。例如,假设我有一个表 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}]

知道如何填写上面的问号以使其正常工作吗?非常感谢!

【问题讨论】:

  • 我认为您可以通过别名:FROM posts oldposts,然后在 RETURNING 子句中引用旧别名和新别名来做到这一点。不过看起来不像。棘手。
  • @CraigRinger 是的,我也试过了。想知道为什么它不能那样工作。
  • 它几乎是 stackoverflow.com/questions/7115941 的重复,没有可接受的答案。

标签: sql postgresql sql-insert sql-returning


【解决方案1】:

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

INSERT 目前不可能做到这一点。 The manual:

表达式可以使用由 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 在每个表中是唯一的):

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小提琴here
sqlfiddle

【讨论】:

  • 很好的答案。第二个查询中的倒数第二行应具有分区子句之外的列别名FROM (SELECT id, row_number() OVER (ORDER BY id) AS rn FROM ins) i
【解决方案2】:

如果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

【讨论】:

  • @ErwinBrandstetter 您对这种方法有何看法?
【解决方案3】:

恕我直言,最简单的解决方案是简单地将一列添加到您的表中,您可以在其中放置被克隆行的 id。

【讨论】:

  • 是的,这可能是最简单的,也可能适合其他 SQL 数据库。但它给 db 架构增加了额外的复杂性。
猜你喜欢
  • 2022-08-02
  • 2023-03-07
  • 1970-01-01
  • 2020-07-13
  • 2011-01-29
  • 1970-01-01
  • 1970-01-01
  • 2016-05-04
  • 2016-06-05
相关资源
最近更新 更多