To powtarzający się problem SELECT or INSERT
, związany z (ale inny niż) UPSERT. Nowa funkcja UPSERT w Postgres 9.5 nadal ma kluczowe znaczenie.
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;
W ten sposób nie piszesz nowej wersji wiersza bez potrzeby.
Jednak , nadal istnieje mały przypadek w przypadku wyścigu . Transakcje współbieżne mogły dodać sprzeczny wiersz, który nie jest jeszcze widoczny w tym samym wyciągu. Następnie INSERT
i SELECT
wyjdź pusty.
Właściwe rozwiązanie dla UPSERT jednorzędowego:
- Czy SELECT lub INSERT w funkcji podatnej na wyścigi?
Ogólne rozwiązania dla UPSERT masowych:
- Jak używać RETURNING z ON CONFLICT w PostgreSQL?
Bez równoczesnego ładowania zapisu
Jeśli współbieżne zapisy (z innej sesji) nie są możliwe, nie trzeba blokować wiersza i można to uprościć:
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;