Tabel users
harus memiliki beberapa kunci utama yang tidak Anda ungkapkan. Untuk tujuan jawaban ini saya akan menamakannya users_id
.
Anda dapat menyelesaikan ini dengan agak elegan dengan CTE pengubah data diperkenalkan dengan PostgreSQL 9.1 :
country
unik
Seluruh operasi agak sepele dalam hal ini:
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;
Anda menyebutkan versi 8.3 dalam pertanyaan Anda. Meningkatkan! Postgres 8.3 telah mencapai akhir masa pakainya.
Bagaimanapun, ini cukup sederhana dengan versi 8.3. Anda hanya perlu dua pernyataan:
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
tidak unik
Itu lebih menantang. Anda bisa cukup buat satu alamat dan tautkan beberapa kali. Tapi Anda memang menyebutkan hubungan 1:1 yang mengesampingkan solusi yang begitu mudah.
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;
Karena tidak ada cara untuk secara jelas menetapkan satu id
dikembalikan dari INSERT
untuk setiap pengguna dalam satu set dengan country
yang identik , saya menggunakan fungsi jendela row_number()
untuk membuatnya unik.
Tidak langsung dengan Postgres 8.3 . Salah satu cara yang mungkin:
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
Ulangi ini sampai NULL
terakhir nilai hilang dari users.address_id
.