PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Insérer des données et définir des clés étrangères avec Postgres

La table users doit avoir une clé primaire que vous n'avez pas révélé. Aux fins de cette réponse, je l'appellerai users_id .

Vous pouvez résoudre ce problème de manière assez élégante avec des CTE modificateurs de données introduit avec PostgreSQL 9.1 :

country est unique

L'ensemble de l'opération est plutôt trivial dans ce cas :

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;

Vous mentionnez la version 8.3 dans votre question. Améliorer! Postgres 8.3 est arrivé en fin de vie.

Quoi qu'il en soit, c'est assez simple avec la version 8.3. Vous avez juste besoin de deux déclarations :

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 n'est pas unique

C'est plus difficile. Vous pourriez créez simplement une adresse et créez un lien vers celle-ci plusieurs fois. Mais vous avez mentionné une relation 1:1 qui exclut une solution aussi pratique.

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;

Comme il n'y a aucun moyen d'attribuer sans ambiguïté exactement un id renvoyé par INSERT à chaque utilisateur d'un ensemble avec un country identique , j'utilise la fonction de fenêtre row_number() pour les rendre uniques.

Pas aussi simple avec Postgres 8.3 . Une manière possible :

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

Répétez ceci jusqu'au dernier NULL la valeur a disparu de users.address_id .