Una soluzione in un'unica istruzione SQL. Richiede PostgreSQL 8.4 o versioni successive.
Considera la seguente demo:
Configurazione di prova:
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');
INSERIRE / SELEZIONA comando:
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;
-
Il primo CTE v non è strettamente necessario, ma realizza che devi inserire i tuoi valori solo una volta.
-
La seconda seleziona CTE s l'
id
datbl
se la "riga" esiste. -
Il terzo Inserimento CTE i la "riga" in
tbl
se (e solo se) non esiste, restituendoid
. -
Il
SELECT
finale restituisce l'id
. Ho aggiunto una colonnasrc
indicando la "fonte" - se la "riga" preesisteva eid
viene da un SELECT, oppure la "riga" era nuova, così come l'id
. -
Questa versione dovrebbe essere il più veloce possibile in quanto non necessita di un ulteriore SELECT da
tbl
e utilizza invece i CTE.
Per renderlo sicuro contro possibili condizioni di gara in un ambiente multiutente:
Anche per le tecniche aggiornate utilizzando il nuovo UPSERT in Postgres 9.5 o successivo:
- SELEZIONA o INSERT in una funzione soggetta a condizioni di gara?