Det är det återkommande problemet med SELECT or INSERT
, relaterad till (men skiljer sig från) en UPSERT. Den nya UPSERT-funktionen i Postgres 9.5 är fortfarande viktig.
WITH ins AS (
INSERT INTO names(name)
VALUES ('bob')
ON CONFLICT ON CONSTRAINT names_name_key DO UPDATE
SET name = NULL
WHERE FALSE -- never executed, but locks the row
RETURNING id
)
SELECT id FROM ins
UNION ALL
SELECT id FROM names
WHERE name = 'bob' -- only executed if no INSERT
LIMIT 1;
På så sätt skriver du faktiskt inte en ny radversion utan behov.
Men , det finns fortfarande ett litet hörnfodral för tävlingsförhållanden . Samtidiga transaktioner kan ha lagt till en motstridig rad, som ännu inte är synlig i samma uttalande. Sedan INSERT
och SELECT
kom upp tom.
Korrekt lösning för enkelrad UPSERT:
- Är SELECT eller INSERT i en funktion utsatt för tävlingsförhållanden?
Allmänna lösningar för bulk UPSERT:
- Hur använder man RETURNING med ON CONFLICT i PostgreSQL?
Utan samtidig skrivbelastning
Om samtidiga skrivningar (från en annan session) inte är möjliga behöver du inte låsa raden och kan förenkla:
WITH ins AS (
INSERT INTO names(name)
VALUES ('bob')
ON CONFLICT ON CONSTRAINT names_name_key DO NOTHING -- no lock needed
RETURNING id
)
SELECT id FROM ins
UNION ALL
SELECT id FROM names
WHERE name = 'bob' -- only executed if no INSERT
LIMIT 1;