Suponha que temos a seguinte tabela:
CREATE TABLE names(
id SERIAL NOT NULL,
CONSTRAINT names__pk PRIMARY KEY(id),
name TEXT NOT NULL,
CONSTRAINT names__name__unq UNIQUE(name)
);
INSERT INTO names(name)
VALUES('apple')
ON CONFLICT(name) DO NOTHING
RETURNING id;
Quando uma linha não existe, RETURNING
a cláusula funciona, mas se já existir uma linha, a consulta não retorna nada. Existe uma maneira de modificar a consulta para que ela sempre retorne o campo id
?
A seguinte consulta única poderia ser usada, para evitar uma segunda consulta na maioria dos casos:
Há uma ressalva quando executado simultaneamente com o mesmo valor. No modo de isolamento padrão de leitura confirmada, se a sessão nº 1 executar esta consulta, mas outra sessão nº 2 tiver inserido o mesmo valor um pouco antes e ainda não confirmada no ponto do INSERT da sessão nº 1, a sessão nº 1 não inserirá o valor (até aí tudo bem), mas a consulta acima retornará um resultado vazio. A razão é que INSERT ON CONFLICT pode ver o valor da sessão não confirmada para detectar o conflito, mas SELECT não pode vê-lo.
Para lidar com isso no lado do cliente, você pode tentar novamente a consulta sempre que ela não retornar nada. Como raramente deveria acontecer, ainda é uma melhoria em termos de executar apenas uma consulta na maioria das vezes, se esse fosse seu objetivo de otimização.
Nos modos de isolamento mais rígidos "Leitura Repetível" ou "Serializável", o problema de simultaneidade seria detectado automaticamente e a transação da sessão nº 1 seria cancelada. Nesse caso também, você normalmente resolve isso tentando novamente.