有效地复制PostgreSQL表中的某些行

EMP*_*EMP 9 sql postgresql sql-insert sql-returning

我有PostgreSQL 9数据库,它使用自动递增整数作为主键.我想复制表中的一些行(基于某些过滤条件),同时更改一个或两个值,即复制除ID(自动生成)和可能的另一列之外的所有列值.

但是,我还希望获得从旧ID到新ID的映射.有没有更好的方法来执行它然后只查询要先复制的行然后一次插入一个新行?

基本上我想做这样的事情:

INSERT INTO my_table (col1, col2, col3)
SELECT col1, 'new col2 value', col3
FROM my_table old
WHERE old.some_criteria = 'something'
RETURNING old.id, id;
Run Code Online (Sandbox Code Playgroud)

然而,这失败了ERROR: missing FROM-clause entry for table "old",我可以看到原因:Postgres必须首先执行SELECT然后插入它,并且RETURNING子句只能访问新插入的行.

Mat*_*ood 9

RETURNING只能引用最终插入行中的列.您不能以这种方式引用"OLD"id,除非表中有一列用于保存它和新ID.

尝试运行它应该工作,并将显示您可以通过RETURNING获得的所有可能的值:

INSERT INTO my_table (col1, col2, col3)
    SELECT col1, 'new col2 value', col3
    FROM my_table AS old
    WHERE old.some_criteria = 'something'
RETURNING *;
Run Code Online (Sandbox Code Playgroud)

它不会让你得到你想要的行为,但应该更好地说明RETURNING如何设计工作.


Erw*_*ter 5

这可以借助数据修改 CTE(Postgres 9.1+)来完成:

WITH sel AS (
   SELECT id, col1, col3
        , row_number() OVER (ORDER BY id) AS rn  -- order any way you like
   FROM   my_table
   WHERE  some_criteria = 'something'
   ORDER  BY id  -- match order or row_number()
   )
,    ins AS (
   INSERT INTO my_table (col1, col2, col3)
   SELECT col1, 'new col2 value', col3
   FROM   sel
   ORDER  BY id  -- redundant to be sure
   RETURNING id
 )
SELECT s.id AS old_id, i.id AS new_id
FROM  (SELECT id, row_number() OVER (ORDER BY id) AS rn FROM ins) i
JOIN   sel s USING (rn);
Run Code Online (Sandbox Code Playgroud)

SQL Fiddle演示。

这依赖于未记录的实现细节,即 a 中的行SELECT按提供的顺序插入(并按提供的顺序返回)。它适用于所有当前版本的 Postgres,并且不会损坏。有关的:

该子句中不允许使用窗口函数RETURNING,因此我row_number()在另一个子查询中应用。

稍后相关答案中有更多解释: