PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

PostgreSQL:inserisci le righe in base alla selezione da un'altra tabella e aggiorna un FK in quella tabella con le righe appena inserite

Esistono diversi modi per risolvere il problema.

1. aggiungi temporaneamente una colonna

Come altri hanno già detto, il modo più semplice è aggiungere temporaneamente una colonna reminder_id al dateset . Completalo con gli IDs originali da reminder tavolo. Usalo per unirti a reminder con il dateset tavolo. Rilascia la colonna temporanea.

2. quando l'inizio è unico

Se i valori di start colonna è unica è possibile farlo senza colonna aggiuntiva unendo reminder tabella con il dateset tabella su start colonna.

INSERT INTO dateset (start)
SELECT start FROM reminder;

WITH
CTE_Joined
AS
(
    SELECT
        reminder.id AS reminder_id
        ,reminder.dateset_id AS old_dateset_id
        ,dateset.id AS new_dateset_id
    FROM
        reminder
        INNER JOIN dateset ON dateset.start = reminder.start
)
UPDATE CTE_Joined
SET old_dateset_id = new_dateset_id
;

3. quando l'inizio non è unico

È possibile farlo senza colonna temporanea anche in questo caso. L'idea principale è la seguente. Diamo un'occhiata a questo esempio:

Abbiamo due righe in reminder con lo stesso start valore e ID 3 e 7:

reminder
id    start         dateset_id
3     2015-01-01    NULL
7     2015-01-01    NULL

Dopo averli inseriti nel dateset , verranno generati nuovi ID, ad esempio 1 e 2:

dateset
id    start
1     2015-01-01
2     2015-01-01

Non importa come colleghiamo queste due righe. Il risultato finale potrebbe essere

reminder
id    start         dateset_id
3     2015-01-01    1
7     2015-01-01    2

o

reminder
id    start         dateset_id
3     2015-01-01    2
7     2015-01-01    1

Entrambe queste varianti sono corrette. Il che ci porta alla seguente soluzione.

Basta inserire prima tutte le righe.

INSERT INTO dateset (start)
SELECT start FROM reminder;

Abbina/unisci due tabelle su start colonna sapendo che non è unico. "Rendilo" unico aggiungendo ROW_NUMBER e uniti da due colonne. È possibile abbreviare la query, ma ho esplicitato ogni passaggio in modo esplicito:

WITH
CTE_reminder_rn
AS
(
    SELECT
        id
        ,start
        ,dateset_id
        ,ROW_NUMBER() OVER (PARTITION BY start ORDER BY id) AS rn
    FROM reminder
)
,CTE_dateset_rn
AS
(
    SELECT
        id
        ,start
        ,ROW_NUMBER() OVER (PARTITION BY start ORDER BY id) AS rn
    FROM dateset
)
,CTE_Joined
AS
(
    SELECT
        CTE_reminder_rn.id AS reminder_id
        ,CTE_reminder_rn.dateset_id AS old_dateset_id
        ,CTE_dateset_rn.id AS new_dateset_id
    FROM
        CTE_reminder_rn
        INNER JOIN CTE_dateset_rn ON 
            CTE_dateset_rn.start = CTE_reminder_rn.start AND
            CTE_dateset_rn.rn = CTE_reminder_rn.rn
)
UPDATE CTE_Joined
SET old_dateset_id = new_dateset_id
;

Spero che sia chiaro dal codice cosa fa, soprattutto quando lo confronti con la versione più semplice senza ROW_NUMBER . Ovviamente, la soluzione complessa funzionerà anche se start è unico, ma non è efficiente come una soluzione semplice.

Questa soluzione presuppone che dateset è vuoto prima di questo processo.