Tabulka users
musí mít nějaký primární klíč které jste neprozradil. Pro účely této odpovědi ji pojmenuji users_id
.
Můžete to poměrně elegantně vyřešit pomocí úprav dat CTE představeno s PostgreSQL 9.1 :
country
je unikátní
Celá operace je v tomto případě poněkud triviální:
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;
Zmiňujete verzi 8.3 ve vaší otázce. Vylepšit! Postgres 8.3 dosáhl konce životnosti.
Ať je to jak chce, s verzí 8.3 je to dostatečně jednoduché. Potřebujete pouze dvě prohlášení:
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
není jedinečný
To je náročnější. Mohli byste stačí vytvořit jednu adresu a několikrát na ni odkazovat. Ale zmínil jste vztah 1:1, který vylučuje takové pohodlné řešení.
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;
Protože neexistuje způsob, jak jednoznačně přiřadit právě jedno id
vrácené z INSERT
každému uživateli v sadě s identickou country
, používám funkci okna row_number()
aby byly jedinečné.
Ne tak přímočaré s Postgres 8.3 . Jeden možný způsob:
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
Opakujte to až do posledního NULL
hodnota je pryč z users.address_id
.