2015-03-25 6 views
6

Sto utilizzando PostgreSQL 9.3.INSERISCI IN ... DA SELEZIONARE ... RESTITUISCE mappature di identificazione

Voglio duplicare alcuni dei record db. Dato che sto utilizzando un id pk di incremento automatico per la tabella, voglio recuperare i mapping degli ID dagli ID generati dei record duplicati a quelli originali. Ad esempio, dire ho una tabella posts con 2 record in esso:

[{'id': 1, 'title': 'first'} 
, {'id': 2. 'title': 'second'}] 

Con SQL:

INSERT INTO posts (title) SELECT title FROM posts RETURNING id, ?? 

mi aspetto di vedere le mappature come:

[{'id': 3, 'from_id': 1} 
, {'id': 4, 'from_id': 2}] 

Qualche idea su come compilare i punti interrogativi sopra per farlo funzionare? Molte grazie!

+0

ti pensato potrebbe essere in grado di farlo tramite aliasing: 'FROM posts oldposts', quindi facendo riferimento ai vecchi e nuovi alias nella clausola' RETURNING'. Comunque non sembra. Difficile. –

+0

@CraigRinger sì, ci ho provato anche io. Ti chiedi perché non funziona in questo modo. – fengye87

+2

è quasi un duplicato di http://stackoverflow.com/questions/7115941 che non ha una risposta accettata. –

risposta

6

Questo sarebbe semplice per UPDATE, dove righe aggiuntive uniti in aggiornamento sono visibili alla clausola RETURNING:

Lo stesso momento non possibile INSERT . Per documentation:

L'espressione può usare tutti i nomi di colonna della tabella denominata da nome_tabella

nome_tabella essere il bersaglio del comando INSERT.

È possibile utilizzare (data-modifying) CTEs per farlo funzionare.
Assumendo title essere unica per query, altra cosa è necessario fare di più:

WITH sel AS (
    SELECT id, title 
    FROM posts 
    WHERE id IN (1,2) -- select rows to copy 
    ) 
, ins AS (
    INSERT INTO posts (title) 
    SELECT title FROM sel 
    RETURNING id, title 
) 
SELECT ins.id, sel.id AS from_id 
FROM ins 
JOIN sel USING (title); 

Se title non è univoco per ogni query (ma almeno id è unico tavolo per):

WITH sel AS (
    SELECT id, title, row_number() OVER (ORDER BY id) AS rn 
    FROM posts 
    WHERE id IN (1,2) -- select rows to copy 
    ORDER BY id 
    ) 
, ins AS (
    INSERT INTO posts (title) 
    SELECT title FROM sel ORDER BY id -- ORDER redundant to be sure 
    RETURNING id 
) 
SELECT i.id, s.id AS from_id 
FROM (SELECT id, row_number() OVER (ORDER BY id) AS rn FROM ins) i 
JOIN sel s USING (rn); 

Questa seconda query si basa sul dettaglio di implementazione non documentato che le righe vengono inserite nell'ordine fornito. Funziona in tutte le versioni correnti di Postgres e probabilmente non si spezzerà.

SQL Fiddle.

+1

ottima risposta. la seconda ultima riga nella seconda query deve avere l'alias di colonna al di fuori della clausola di partizione 'FROM (ID SELECT, row_number() OVER (ID OR BY BY) AS rn FROM ins) i' – salient

+0

@salient: Giusto, grazie, corretto. –

0

La più semplice soluzione secondo me sarebbe quella di aggiungere semplicemente una colonna al vostro tavolo dove si potrebbe mettere id della riga che è stata clonata.

+0

Sì, questo potrebbe essere il più semplice e potrebbe anche adattarsi ad altri dbs SQL. Ma aggiunge ulteriore complessità allo schema db. – fengye87

1

se id colonna di posts generato da nextval('posts_id_seq'::regclass) si può chiamare questa funzione a mano per ogni nuova riga

with 
sel as (
    SELECT id, title, nextval('posts_id_seq'::regclass) new_id 
    FROM posts 
    WHERE id IN (1,2) 
), 
ins as (
    INSERT INTO posts (id, title) 
    SELECT new_id, title 
    FROM sel 
) 
SELECT id, new_id 
FROM sel 

it'l funziona per tutti i dati, includere non univoco title