En lösning i en enda SQL-sats. Kräver PostgreSQL 8.4 eller senare dock.
Tänk på följande demo:
Testinställningar:
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örsta CTE v är inte strikt nödvändigt, men uppnår att du måste ange dina värden bara en gång.
-
Den andra CTE s väljer
id
fråntbl
om "raden" finns. -
Den tredje CTE i infogar "raden" till
tbl
om (och bara om) det inte finns, returnerarid
. -
Den sista
SELECT
returnerarid
. Jag lade till en kolumnsrc
anger "källan" - om "raden" existerade ochid
kommer från en SELECT, eller så var "raden" ny och så ärid
. -
Denna version bör vara så snabb som möjligt eftersom den inte behöver en extra SELECT från
tbl
och använder CTE istället.
För att göra detta säkert mot möjliga tävlingsförhållanden i en miljö med flera användare:
Också för uppdaterade tekniker med den nya UPSERT i Postgres 9.5 eller senare:
- Är SELECT eller INSERT i en funktion utsatt för tävlingsförhållanden?