Tabellen users
skal have en primær nøgle som du ikke har oplyst. Til formålet med dette svar vil jeg navngive det users_id
.
Du kan løse dette ret elegant med datamodificerende CTE'er introduceret med PostgreSQL 9.1 :
country
er unik
Hele operationen er ret triviel i dette tilfælde:
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;
Du nævner version 8.3 i dit spørgsmål. Opgrader! Postgres 8.3 har nået slutningen af livet.
Hvorom alting er, så er dette simpelt nok med version 8.3. Du skal blot bruge to udsagn:
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
er ikke unik
Det er mere udfordrende. Du kunne bare opret én adresse og link til den flere gange. Men du nævnte et 1:1 forhold, der udelukker en så bekvem løsning.
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;
Da der ikke er nogen måde at entydigt tildele præcis ét id
returneret fra INSERT
til hver bruger i et sæt med identisk country
, jeg bruger vinduesfunktionen row_number()
for at gøre dem unikke.
Ikke så ligetil med Postgres 8.3 . En mulig måde:
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
Gentag dette indtil den sidste NULL
værdien er væk fra users.address_id
.