12

我有使用自动递增整数作为主键的 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子句只能访问新插入的行。

4

5 回答 5

10

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 是如何设计的。

于 2011-08-21T00:09:55.130 回答
4

这可以在数据修改 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演示。

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

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

此相关后续答案中的更多解释:

于 2015-03-25T18:51:47.243 回答
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 *;

谢谢!

于 2013-04-17T14:47:21.597 回答
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';
于 2014-01-30T21:15:36.877 回答
0

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

于 2011-08-22T13:03:32.337 回答