sql >> Databasteknik >  >> RDS >> PostgreSQL

Infoga data och ställ in främmande nycklar med Postgres

Tabellen users måste ha någon primär nyckel som du inte avslöjat. För detta svar kommer jag att namnge det users_id .

Du kan lösa detta ganska elegant med datamodifierande CTE:er introducerad med PostgreSQL 9.1 :

country är unik

Hela operationen är ganska trivial i det här fallet:

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ämner version 8.3 i din fråga. Uppgradera! Postgres 8.3 har nått slutet på livet.

Hur som helst, detta är enkelt nog med version 8.3. Du behöver bara två påståenden:

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 är inte unik

Det är mer utmanande. Du kunde skapa bara en adress och länka till den flera gånger. Men du nämnde ett 1:1-förhållande som utesluter en så bekväm 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;

Eftersom det inte finns något sätt att entydigt tilldela exakt ett id returneras från INSERT till varje användare i en uppsättning med identiskt country , jag använder fönsterfunktionen row_number() för att göra dem unika.

Inte lika enkelt med Postgres 8.3 . Ett möjligt sätt:

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

Upprepa detta tills den sista NULL värdet är borta från users.address_id .




  1. PERIOD_ADD() Exempel – MySQL

  2. Hur man lagrar data i unicode på hindi

  3. Installera Oracle 11g Release 2 Enterprise Edition på Windows 7 32-bitars

  4. Hur uppdaterar jag Identity Column i SQL Server?