2011-08-19 16 views
8

Ho un database PostgreSQL 9 che utilizza numeri interi a incremento automatico come chiavi primarie. Voglio duplicare alcune delle righe in una tabella (in base ad alcuni criteri di filtro), cambiando uno o due valori, ovvero copiando tutti i valori di colonna, ad eccezione dell'ID (che viene generato automaticamente) e probabilmente di un'altra colonna.Duplica efficacemente alcune righe nella tabella PostgreSQL

Tuttavia, voglio anche ottenere la mappatura dal vecchio al nuovo ID. C'è un modo migliore per farlo, basta interrogare le righe per copiare prima e poi inserire nuove righe una alla volta?

In sostanza voglio fare qualcosa di simile:

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; 

Tuttavia, questo non riesce con ERROR: missing FROM-clause entry for table "old" e posso capire perché: Postgres deve fare la prima SELECT e poi inserendolo ei RETURNING clausole ha solo l'accesso alla riga appena inserita.

+0

Ti capita di avere idea di come fare la vostra richiesta w/o nominare tutti gli attributi che voglio copiare? Ho un tavolo abbastanza grande (con molti attributi) e li scrivo tutti e non dimenticare che uno è un dolore ... –

+0

perché due domande sulla stessa cosa? http://stackoverflow.com/questions/29256888/insert-into-from-select-returning-id-mappings – murison

risposta

8

RITORNO può riferirsi solo alle colonne nella riga finale inserita. Non puoi fare riferimento all'ID "OLD" in questo modo, a meno che non ci sia una colonna nella tabella per contenere sia essa che il nuovo ID.

Provare a eseguire questo che dovrebbe funzionare e mostrerà tutti i valori possibili che si possono ottenere via RITORNO:

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

Non si otterrà il comportamento che si desidera, ma dovrebbe illustrare meglio come il ritorno è stato progettato lavorare.

+0

Grazie - ma credo di aver già capito come funziona il RETURNING. – EMP

0

"vecchio" è una parola riservata, utilizzata dal sistema di riscrittura delle regole. [presumo che questo frammento di query non faccia parte di una regola; in tal caso avresti formulato la domanda in modo diverso]

+0

Sì, hai ragione - ho inventato la query per il post in quanto quella reale è più complessa. – EMP

2

Buono! I test di questo codice, ma a cambiare questo (FROM my_table AS old) a (FROM my_table) e questo (WHERE old.some_criteria = 'something') a (WHERE some_criteria = 'something')

Questo è il codice finale che uso

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

Grazie!

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; 

aggiungere un altro aggiornamento prima l'inserto di modificare un altro campo

UPDATE tmptable SET another = 'data'; 
+0

'UPDATE tmptable SET id =' funziona solo se id è un numero intero, non se è seriale – DarkMukke

2

Questo può essere fatto con l'aiuto di CTE dati-modifiying (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); 

dimostrazione SQL Fiddle.

Questo si basa sul dettaglio di implementazione non documentato che le righe da un SELECT vengono inserite nell'ordine fornito (e restituite nell'ordine fornito). Funziona in tutte le versioni correnti di Postgres e non si spezzerà.Related:

funzioni di finestra non sono ammessi nella clausola RETURNING, quindi si applicano row_number() in un'altra sottoquery.

Più spiegazione in questa risposta in seguito relativi: