De tabel users
moet een primaire sleutel hebben die je niet hebt bekendgemaakt. Voor dit antwoord noem ik het users_id
.
Je kunt dit vrij elegant oplossen met gegevensmodificerende CTE's geïntroduceerd met PostgreSQL 9.1 :
country
is uniek
De hele operatie is in dit geval nogal triviaal:
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;
U vermeldt versie 8.3 bij uw vraag. Upgrade! Postgres 8.3 heeft het einde van de levensduur bereikt.
Hoe het ook zij, dit is eenvoudig genoeg met versie 8.3. U hebt slechts twee verklaringen nodig:
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
is niet uniek
Dat is uitdagender. Je zou maak gewoon één adres aan en link er meerdere keren naar. Maar je noemde wel een 1:1-relatie die zo'n handige oplossing uitsluit.
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;
Aangezien er geen manier is om precies één id
ondubbelzinnig toe te wijzen teruggestuurd van de INSERT
aan elke gebruiker in een set met identiek country
, ik gebruik de vensterfunctie row_number()
om ze uniek te maken.
Niet zo eenvoudig met Postgres 8.3 . Een mogelijke manier:
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
Herhaal dit tot de laatste NULL
waarde is verdwenen van users.address_id
.