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

时间:2011-08-19 01:06:13

标签: 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;

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

5 个答案:

答案 0 :(得分: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 *;

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

答案 1 :(得分:2)

好!我测试了这段代码,但是我改变了 这个(FROM my_table AS old)在(FROM my_table)和 (WHERE old.some_criteria = 'something'

中的这个(WHERE some_criteria = 'something'

这是我使用的最终代码

INSERT INTO my_table (col1, col2, col3)
    SELECT col1, 'new col2 value', col3
    FROM my_table AS old
    WHERE some_criteria = 'something'
RETURNING *;

谢谢!

答案 2 :(得分:2)

这可以在数据修改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);

SQL Fiddle演示。

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

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

后面这个相关答案的更多解释:

答案 3 :(得分:0)

'old'是保留字,由规则重写系统使用。 [我认为这个查询片段不是规则的一部分;在这种情况下,你会以不同的方式表达问题]

答案 4 :(得分:0)

DROP TABLE IF EXISTS tmptable;
CREATE TEMPORARY TABLE tmptable as SELECT * FROM products WHERE id = 100;
UPDATE tmptable SET id = sbq.id from (select max(id)+1 as id from products) as sbq;
INSERT INTO products (SELECT * FROM tmptable);
DROP TABLE IF EXISTS tmptable;

在插入之前添加另一个更新以修改另一个字段

UPDATE tmptable SET another = 'data';