Эффективно продублировать некоторые строки в таблице PostgreSQL - PullRequest
9 голосов
/ 19 августа 2011

У меня есть база данных PostgreSQL 9, которая использует автоинкрементные целые числа в качестве первичных ключей.Я хочу продублировать некоторые строки в таблице (основываясь на некоторых критериях фильтра), изменяя одно или два значения, т.е. копировать все значения столбцов, кроме идентификатора (который создается автоматически) и, возможно, другого столбца.

Однако я также хочу получить сопоставление от старых идентификаторов к новым.Есть ли лучший способ сделать это, затем просто запросить сначала строки для копирования, а затем вставить новые строки по одной за раз?

По сути, я хочу сделать что-то вроде этого:

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 ]

9 голосов
/ 21 августа 2011

RETURNING может ссылаться только на столбцы в последней вставленной строке.Вы не можете ссылаться на «старый» идентификатор таким образом, если в таблице нет столбца для хранения и нового, и нового идентификатора.

Попробуйте выполнить это, которое должно работать и покажет все возможные значения, которые вы можетеполучить через 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 предназначен для работы.

2 голосов
/ 25 марта 2015

Это можно сделать с помощью 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() в другом подзапросе.

Дополнительные объяснения в следующем ответе:

2 голосов
/ 17 апреля 2013

Хорошо! Я тестирую этот код, но меняю это (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 *;

Спасибо!

0 голосов
/ 31 января 2014
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';
0 голосов
/ 22 августа 2011

'old' - зарезервированное слово, используемое системой переписывания правил.[Я предполагаю, что этот фрагмент запроса не является частью правила;в этом случае вы бы сформулировали вопрос по-другому]

...