Uma solução em uma única instrução SQL. Requer PostgreSQL 8.4 ou mais tarde.
Considere a seguinte demonstração:
Configuração de teste:
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');
Comando INSERIR/SELECIONAR:
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;
-
O primeiro CTE v não é estritamente necessário, mas permite que você insira seus valores apenas uma vez.
-
A segunda seleções de CTEs oid
detbl
se a "linha" existir.
-
A terceira inserção CTE i a "linha" emtbl
se (e somente se) não existir, retornandoid
.
-
OSELECT
final retorna oid
. Eu adicionei uma colunasrc
indicando a "fonte" - se a "linha" já existia eid
vem de um SELECT, ou a "linha" era nova e oid
também .
-
Esta versão deve ser o mais rápida possível, pois não precisa de um SELECT adicional detbl
e usa os CTEs em vez disso.
Para tornar isso seguro contra possíveis condições de corrida em um ambiente multiusuário:
Também para técnicas atualizadas usando o novo UPSERT no Postgres 9.5 ou mais tarde:
- O SELECT ou INSERT está em uma função propensa a condições de corrida?