sql >> Database >  >> RDS >> PostgreSQL

Gegevens invoegen en externe sleutels instellen met Postgres

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 .




  1. Hoe MID() werkt in MariaDB

  2. Dieper graven in Django-migraties

  3. Een subset van kolommen selecteren uit de resultatenset van een opgeslagen procedure (T-SQL)

  4. Oracle join-syntaxisvoorbeelden