Je to opakující se problém SELECT or INSERT
, týkající se (ale odlišný od) UPSERT. Nová funkce UPSERT v Postgres 9.5 je stále nápomocná.
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;
Tímto způsobem ve skutečnosti nenapíšete novou verzi řádku bez potřeby.
Nicméně , stále existuje malé rohové pouzdro pro závodní podmínky . Souběžné transakce mohly přidat konfliktní řádek, který ještě není viditelný ve stejném příkazu. Poté INSERT
a SELECT
přijít prázdný.
Správné řešení pro jednořadý UPSERT:
- Je SELECT nebo INSERT ve funkci náchylný k závodům?
Obecná řešení pro hromadné UPSERT:
- Jak používat RETURNING s ON CONFLICT v PostgreSQL?
Bez souběžného zatížení zápisu
Pokud souběžné zápisy (z jiné relace) nejsou možné, nemusíte řádek zamykat a můžete to zjednodušit:
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;