PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Retorna id se existir uma linha, INSERT caso contrário


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 o id de tbl se a "linha" existir.

  • A terceira inserção CTE i a "linha" em tbl se (e somente se) não existir, retornando id .

  • O SELECT final retorna o id . Eu adicionei uma coluna src indicando a "fonte" - se a "linha" já existia e id vem de um SELECT, ou a "linha" era nova e o id também .

  • Esta versão deve ser o mais rápida possível, pois não precisa de um SELECT adicional de tbl 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?