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
.