En løsning i en enkelt SQL-sætning. Kræver PostgreSQL 8.4 eller senere.
Overvej følgende demo:
Testopsætning:
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 kommando:
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;
-
Den første CTE v er ikke strengt nødvendigt, men opnår, at du skal indtaste dine værdier kun én gang.
-
Den anden CTE s vælger
id
fratbl
hvis "rækken" findes. -
Den tredje CTE i indsætter "rækken" ind i
tbl
hvis (og kun hvis) det ikke eksisterer, returnererid
. -
Den endelige
SELECT
returnererid
. Jeg tilføjede en kolonnesrc
angiver "kilden" - om "rækken" eksisterede i forvejen ogid
kommer fra en SELECT, eller "rækken" var ny, og det samme erid
. -
Denne version skal være så hurtig som muligt, da den ikke behøver en ekstra SELECT fra
tbl
og bruger CTE'erne i stedet.
For at gøre dette sikkert mod mulige løbsforhold i et flerbrugermiljø:
Også for opdaterede teknikker ved hjælp af den nye UPSERT i Postgres 9.5 eller senere:
- Er SELECT eller INSERT i en funktion, der er tilbøjelig til løbsforhold?