sql >> Database teknologi >  >> RDS >> PostgreSQL

Indsæt data og indstil fremmednøgler med Postgres

Tabellen users skal have en primær nøgle som du ikke har oplyst. Til formålet med dette svar vil jeg navngive det users_id .

Du kan løse dette ret elegant med datamodificerende CTE'er introduceret med PostgreSQL 9.1 :

country er unik

Hele operationen er ret triviel i dette tilfælde:

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;

Du nævner version 8.3 i dit spørgsmål. Opgrader! Postgres 8.3 har nået slutningen af ​​livet.

Hvorom alting er, så er dette simpelt nok med version 8.3. Du skal blot bruge to udsagn:

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 er ikke unik

Det er mere udfordrende. Du kunne bare opret én adresse og link til den flere gange. Men du nævnte et 1:1 forhold, der udelukker en så bekvem løsning.

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;

Da der ikke er nogen måde at entydigt tildele præcis ét id returneret fra INSERT til hver bruger i et sæt med identisk country , jeg bruger vinduesfunktionen row_number() for at gøre dem unikke.

Ikke så ligetil med Postgres 8.3 . En mulig måde:

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

Gentag dette indtil den sidste NULL værdien er væk fra users.address_id .




  1. Sådan konfigureres en ekstern MySQL-forbindelse

  2. Hvad betyder importfejl:Symbol ikke fundet:_PQencryptPasswordConn, og hvordan retter jeg det?

  3. freeTDS bruger ikke sin konfiguration

  4. MariaDB SUBSTR() Forklaret