Rozwiązanie w jednej instrukcji SQL. Wymaga PostgreSQL 8.4 lub później.
Rozważ następującą prezentację:
Konfiguracja testowa:
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');
WSTAW / WYBIERZ polecenie:
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;
-
Pierwsze CTE v nie jest bezwzględnie konieczne, ale osiąga, że musisz wprowadzić swoje wartości tylko raz.
-
Drugi wybór CTE
id
ztbl
jeśli „wiersz” istnieje. -
Trzecie wstawia CTE i "wiersz" do
tbl
jeśli (i tylko jeśli) nie istnieje, zwracającid
. -
Ostatni
SELECT
zwracaid
. Dodałem kolumnęsrc
wskazując "źródło" - czy "wiersz" istniał wcześniej iid
pochodzi z SELECT lub „wiersz” był nowy, podobnie jakid
. -
Ta wersja powinna być tak szybka, jak to możliwe, ponieważ nie wymaga dodatkowego SELECT z
tbl
i zamiast tego używa CTE.
Aby zabezpieczyć to przed możliwymi warunkami wyścigu w środowisku wielu użytkowników:
Również dla zaktualizowanych technik przy użyciu nowego UPSERT w Postgres 9.5 lub później:
- Czy SELECT lub INSERT w funkcji podatnej na wyścigi?