テーブルusers
主キーが必要です あなたが開示しなかったこと。この回答のために、users_id
という名前を付けます。 。
データ変更CTEを使用すると、これをかなりエレガントに解決できます。 PostgreSQLで導入9.1 :
country
ユニークです
この場合、操作全体はかなり簡単です。
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;
あなたはバージョン8.3に言及します あなたの質問で。アップグレード! Postgres8.3は寿命に達しました。
とはいえ、これはバージョン8.3では十分に単純です。必要なステートメントは2つだけです:
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
ユニークではありません
それはもっと難しいです。あなたはできた 1つのアドレスを作成し、それに複数回リンクするだけです。しかし、あなたはそのような便利な解決策を除外する1:1の関係について言及しました。
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;
正確に1つのid
を明確に割り当てる方法はないため INSERT
から返されます 同一のcountry
を持つセット内のすべてのユーザーに 、ウィンドウ関数row_number()
を使用します それらをユニークにするために。
Postgres 8.3ではそれほど単純ではありません 。考えられる1つの方法:
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
これを繰り返す 最後のNULL
まで 値がusers.address_id
から削除されました 。