PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Geben Sie die ID zurück, wenn eine Zeile vorhanden ist, andernfalls INSERT

Eine Lösung in einer einzigen SQL-Anweisung. Erfordert PostgreSQL 8.4 oder später.
Betrachten Sie die folgende Demo:

Testaufbau:

CREATE TEMP TABLE tbl (
  id  serial PRIMARY KEY
 ,txt text   UNIQUE   -- obviously there is unique column (or set of columns)
);

INSERT INTO tbl(txt) VALUES ('one'), ('two');

INSERT / SELECT-Befehl:

WITH v AS (SELECT 'three'::text AS txt)
    ,s AS (SELECT id FROM tbl JOIN v USING (txt))
    ,i AS (
       INSERT INTO tbl (txt)
       SELECT txt
       FROM   v
       WHERE  NOT EXISTS (SELECT * FROM s)
       RETURNING id
       )
SELECT id, 'i'::text AS src FROM i
UNION  ALL
SELECT id, 's' FROM s;
  • Der erste CTE v ist nicht zwingend erforderlich, bewirkt aber, dass Sie Ihre Werte eingeben müssen nur einmal.

  • Der zweite CTE s wählt aus die id aus tbl wenn die "Zeile" existiert.

  • Der dritte CTE i fügt ein die "Zeile" in tbl wenn (und nur wenn) es nicht existiert, wird id zurückgegeben .

  • Das letzte SELECT gibt die id zurück . Ich habe eine Spalte src hinzugefügt Angabe der "Quelle" - ob die "Zeile" bereits existierte und id stammt aus einem SELECT, oder die "Zeile" war neu und ebenso die id .

  • Diese Version sollte so schnell wie möglich sein, da sie kein zusätzliches SELECT von tbl benötigt und verwendet stattdessen die CTEs.

Um dies gegen mögliche Racebedingungen in einer Umgebung mit mehreren Benutzern abzusichern:
Auch für aktualisierte Techniken mit dem neuen UPSERT in Postgres 9.5 oder später:

  • Ist SELECT oder INSERT in einer Funktion anfällig für Race-Conditions?