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

Inserisci dati e imposta chiavi esterne con Postgres

La tabella users deve avere una chiave primaria che non hai rivelato. Ai fini di questa risposta la chiamerò users_id .

Puoi risolverlo in modo piuttosto elegante con CTE di modifica dei dati introdotto con PostgreSQL 9.1 :

country è unico

L'intera operazione è piuttosto banale in questo caso:

WITH i AS (
    INSERT INTO addresses (country) 
    SELECT country
    FROM   users
    WHERE  address_id IS NULL 
    RETURNING id, country
    )
UPDATE users u
SET    address_id = i.id
FROM   i
WHERE  i.country = u.country;

Hai menzionato la versione 8.3 nella tua domanda Aggiornamento! Postgres 8.3 ha raggiunto la fine del ciclo di vita.

Comunque sia, questo è abbastanza semplice con la versione 8.3. Hai solo bisogno di due affermazioni:

INSERT INTO addresses (country) 
SELECT country
FROM   users
WHERE  address_id IS NULL;

UPDATE users u
SET    address_id = a.id
FROM   addresses a
WHERE  address_id IS NULL 
AND    a.country = u.country;

country non è unico

Questo è più impegnativo. Potresti basta creare un indirizzo e collegarlo più volte. Ma hai menzionato una relazione 1:1 che esclude una soluzione così conveniente.

WITH s AS (
    SELECT users_id, country
         , row_number() OVER (PARTITION BY country) AS rn
    FROM   users
    WHERE  address_id IS NULL 
    )
    , i AS (
    INSERT INTO addresses (country) 
    SELECT country
    FROM   s
    RETURNING id, country
    )
    , r AS (
    SELECT *
         , row_number() OVER (PARTITION BY country) AS rn
    FROM   i
    )
UPDATE users u
SET    address_id = r.id
FROM   r
JOIN   s USING (country, rn)    -- select exactly one id for every user
WHERE  u.users_id = s.users_id
AND    u.address_id IS NULL;

Poiché non è possibile assegnare in modo inequivocabile esattamente un id restituito da INSERT a tutti gli utenti in un set con country identico , io uso la funzione della finestra row_number() per renderli unici.

Non così semplice con Postgres 8.3 . Un modo possibile:

INSERT INTO addresses (country) 
SELECT DISTINCT country -- pick just one per set of dupes
FROM   users
WHERE  address_id IS NULL;

UPDATE users u
SET    address_id = a.id
FROM   addresses a
WHERE  a.country = u.country
AND    u.address_id IS NULL
AND NOT EXISTS (
    SELECT * FROM addresses b
    WHERE  b.country = a.country
    AND    b.users_id < a.users_id
    ); -- effectively picking the smallest users_id per set of dupes

Ripeti questo fino all'ultimo NULL il valore è andato da users.address_id .