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

Hvordan UPSERT (FLETT, INDSÆT ... PÅ DUPLIKAT OPDATERING) i PostgreSQL?

9.5 og nyere:

PostgreSQL 9.5 og nyere understøttelse INSERT ... ON CONFLICT (key) DO UPDATE (og ON CONFLICT (key) DO NOTHING ), dvs. upsert.

Sammenligning med ON DUPLICATE KEY UPDATE .

Hurtig forklaring.

For brug se manualen - specifikt conflict_action klausul i syntaksdiagrammet og den forklarende tekst.

I modsætning til løsningerne til 9.4 og ældre, der er angivet nedenfor, fungerer denne funktion med flere modstridende rækker, og den kræver ikke eksklusiv låsning eller en genforsøgsløkke.

Forpligtelsen til at tilføje funktionen er her, og diskussionen omkring dens udvikling er her.

Hvis du er på 9.5 og ikke behøver at være bagudkompatibel, kan du stoppe med at læse nu .

9.4 og ældre:

PostgreSQL har ikke nogen indbygget UPSERT (eller MERGE ) facilitet, og at gøre det effektivt i lyset af samtidig brug er meget vanskeligt.

Denne artikel diskuterer problemet i nyttige detaljer.

Generelt skal du vælge mellem to muligheder:

  • Individuelle indsættelses-/opdateringsoperationer i en genforsøgsløkke; eller
  • Låser bordet og laver batchfletning

Individuel række genforsøg loop

Brug af individuelle række-upserts i en genforsøgsløkke er den rimelige mulighed, hvis du vil have mange forbindelser, der samtidig forsøger at udføre indsættelser.

PostgreSQL-dokumentationen indeholder en nyttig procedure, der lader dig gøre dette i en løkke inde i databasen. Den beskytter mod tabte opdateringer og indsæt racer, i modsætning til de fleste naive løsninger. Det vil kun virke i READ COMMITTED tilstand og er kun sikker, hvis det er det eneste, du gør i transaktionen. Funktionen fungerer ikke korrekt, hvis triggere eller sekundære unikke nøgler forårsager unikke overtrædelser.

Denne strategi er meget ineffektiv. Når det er praktisk, bør du stille arbejdet i kø og i stedet lave en bulk-upsert som beskrevet nedenfor.

Mange forsøg på løsninger på dette problem tager ikke hensyn til tilbagerulninger, så de resulterer i ufuldstændige opdateringer. To transaktioner kapløb med hinanden; en af ​​dem INSERT s; den anden får en dubletnøglefejl og laver en UPDATE i stedet. UPDATE blokke, der venter på INSERT at rulle tilbage eller begå. Når den ruller tilbage, vises UPDATE betingelsesgenkontrol matcher nul rækker, så selvom UPDATE begår, at det faktisk ikke har gjort det oprør, du forventede. Du er nødt til at kontrollere resultatrækketællingerne og prøve igen, hvor det er nødvendigt.

Nogle forsøg på løsninger tager heller ikke hensyn til SELECT-løb. Hvis du prøver det åbenlyse og enkle:

-- THIS IS WRONG. DO NOT COPY IT. It's an EXAMPLE. BEGIN; UPDATE testtable SET somedata = 'blah' WHERE id = 2; -- Remember, this is WRONG. Do NOT COPY IT. INSERT INTO testtable (id, somedata) SELECT 2, 'blah' WHERE NOT EXISTS (SELECT 1 FROM testtable WHERE testtable.id = 2); COMMIT;

så når to kører på én gang er der flere fejltilstande. Det ene er det allerede diskuterede problem med en opdateringskontrol. En anden er, hvor både UPDATE på samme tid, matchende nul rækker og fortsætter. Så laver de begge EXISTS test, som sker før INSERT . Begge får nul rækker, så begge udfører INSERT . Man fejler med en dubletnøglefejl.

Det er derfor, du har brug for en genforsøgsløkke. Du tror måske, at du kan forhindre duplikerede nøglefejl eller mistede opdateringer med smart SQL, men det kan du ikke. Du skal kontrollere rækkeantal eller håndtere duplikerede nøglefejl (afhængigt af den valgte tilgang) og prøve igen.

Lad være med at rulle din egen løsning til dette. Ligesom med beskedkø, er det sandsynligvis forkert.

Masseopskæring med lås

Nogle gange vil man lave en bulk upsert, hvor man har et nyt datasæt, som man vil flette ind i et ældre eksisterende datasæt. Dette er svært mere effektiv end individuelle row upserts og bør foretrækkes, når det er praktisk muligt.

I dette tilfælde følger du typisk følgende proces:

  • CREATE en TEMPORARY bord

  • COPY eller masseindsæt de nye data i den midlertidige tabel

  • LOCK måltabellen IN EXCLUSIVE MODE . Dette tillader andre transaktioner at SELECT , men foretag ingen ændringer i tabellen.

  • Foretag en UPDATE ... FROM af eksisterende poster ved hjælp af værdierne i temp-tabellen;

  • Lav en INSERT rækker, der ikke allerede findes i måltabellen;

  • COMMIT , udløser låsen.

For eksempel, for eksemplet givet i spørgsmålet, ved at bruge INSERT med flere værdier for at udfylde den midlertidige tabel:

BEGIN;

CREATE TEMPORARY TABLE newvals(id integer, somedata text);

INSERT INTO newvals(id, somedata) VALUES (2, 'Joe'), (3, 'Alan');

LOCK TABLE testtable IN EXCLUSIVE MODE;

UPDATE testtable
SET somedata = newvals.somedata
FROM newvals
WHERE newvals.id = testtable.id;

INSERT INTO testtable
SELECT newvals.id, newvals.somedata
FROM newvals
LEFT OUTER JOIN testtable ON (testtable.id = newvals.id)
WHERE testtable.id IS NULL;

COMMIT;
 

Relateret læsning

  • UPSERT wiki-side
  • UPSERTisms i Postgres
  • Indsæt, ved dubletopdatering i PostgreSQL?
  • http://petereisentraut.blogspot.com/2010/05/merge-syntax.html
  • Oprør med en transaktion
  • Er SELECT eller INSERT i en funktion, der er tilbøjelig til løbsforhold?
  • SQL MERGE på PostgreSQL-wikien
  • Mest idiomatisk måde at implementere UPSERT i Postgresql i dag

Hvad med MERGE ?

SQL-standard MERGE har faktisk dårligt defineret concurrency semantik og er ikke egnet til upserting uden at låse en tabel først.

Det er en virkelig nyttig OLAP-erklæring til datasammenfletning, men det er faktisk ikke en nyttig løsning til samtidighedssikker upsert. Der er masser af råd til folk, der bruger andre DBMS'er til at bruge MERGE for upserts, men det er faktisk forkert.

Andre DB'er:

  • INSERT ... ON DUPLICATE KEY UPDATE i MySQL
  • MERGE fra MS SQL Server (men se ovenfor om MERGE problemer)
  • MERGE fra Oracle (men se ovenfor om MERGE problemer)


  1. Transponer rækker og kolonner uden aggregat

  2. Kan jeg bruge ADFS 2.0 til at godkende visse brugere mod SQL Server?

  3. Aggreger bitvis-ELLER i en underforespørgsel

  4. Deltag i borde fra to forskellige servere