sql >> Databasteknik >  >> RDS >> PostgreSQL

PostgreSQL - infoga rader baserat på val från en annan tabell, och uppdatera en FK i den tabellen med de nyinfogade raderna

Det finns flera sätt att lösa problemet.

1. tillfälligt lägga till en kolumn

Som andra nämnt är det enkla sättet att tillfälligt lägga till en kolumn reminder_id till dateset . Fyll den med original IDs från reminder tabell. Använd den för att gå med i reminder med dateset tabell. Släpp den tillfälliga kolumnen.

2. när start är unik

Om värden för start kolumnen är unik det är möjligt att göra det utan extra kolumn genom att gå med i reminder tabell med dateset tabellen på start kolumn.

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. när start inte är unik

Det är möjligt att göra det utan tillfällig kolumn även i detta fall. Huvudtanken är följande. Låt oss ta en titt på detta exempel:

Vi har två rader i reminder med samma start värde och ID 3 och 7:

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

Efter att vi har infogat dem i dateset , kommer det att skapas nya ID, till exempel 1 och 2:

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

Det spelar egentligen ingen roll hur vi länkar dessa två rader. Slutresultatet kan bli

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

eller

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

Båda dessa varianter är korrekta. Vilket leder oss till följande lösning.

Sätt bara in alla rader först.

INSERT INTO dateset (start)
SELECT start FROM reminder;

Matcha/ansluta två bord på start kolumn vet att det inte är unikt. "Gör det" unikt genom att lägga till ROW_NUMBER och sammanfogning med två kolumner. Det är möjligt att göra frågan kortare, men jag stavade uttryckligen varje steg:

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
;

Jag hoppas att det framgår av koden vad den gör, särskilt när du jämför den med den enklare versionen utan ROW_NUMBER . Uppenbarligen kommer den komplexa lösningen att fungera även om start är unik, men den är inte lika effektiv som en enkel lösning.

Denna lösning förutsätter att dateset är tom före denna process.



  1. Varför lägger Rails till `OR 1=0` till frågor med hjälp av where-satsens hash-syntax med ett intervall?

  2. Mina favoritpostgreSQL-frågor och varför de är viktiga

  3. Optimera frågor baserade på klustrade och icke-klustrade index i SQL?

  4. Summa värden för flerdimensionell array för nyckel utan loop