Problema
O manual explica:
O opcionalRETURNING
cláusula faz com queUPDATE
para calcular e retornar valor(es) com base em cada linha realmente atualizada. Qualquer expressão usando as colunas da tabela e/ou colunas de outras tabelas mencionadas emFROM
, pode ser calculado. Os novos valores (pós-atualização) das colunas da tabela são usados . A sintaxe doRETURNING
lista é idêntica à lista de saída deSELECT
.
Minha ênfase em negrito. Não há como acessar a linha antiga em um
RETURNING
cláusula. Você pode contornar essa restrição com um gatilho ou um SELECT
separado antes a UPDATE
envolto em uma transação ou envolto em um CTE como foi comentado. No entanto, o que você está tentando alcançar funciona perfeitamente se você se juntar a outra instância da tabela no
FROM
cláusula:Solução sem gravações simultâneas
UPDATE tbl x
SET tbl_id = 23
, name = 'New Guy'
FROM tbl y -- using the FROM clause
WHERE x.tbl_id = y.tbl_id -- must be UNIQUE NOT NULL
AND x.tbl_id = 3
RETURNING y.tbl_id AS old_id, y.name AS old_name
, x.tbl_id , x.name;
Devoluções:
old_id | old_name | tbl_id | name
--------+----------+--------+---------
3 | Old Guy | 23 | New Guy
A(s) coluna(s) usada(s) para auto-junção deve(m) ser
UNIQUE NOT NULL
. No exemplo simples, o WHERE
condição está na mesma coluna tbl_id
, mas isso é apenas coincidência. Funciona para qualquer condições. Eu testei isso com as versões do PostgreSQL de 8.4 a 13.
É diferente para
INSERT
:- INSERT INTO ... FROM SELECT ... RETURNING id mapeamentos
Soluções com carga de gravação simultânea
Existem várias maneiras de evitar condições de corrida com operações de gravação simultâneas nas mesmas linhas. (Observe que operações de gravação simultâneas em linhas não relacionadas não são problema algum.) O método simples, lento e seguro (mas caro) é executar a transação com
SERIALIZABLE
nível de isolamento:BEGIN ISOLATION LEVEL SERIALIZABLE;
UPDATE ... ;
COMMIT;
Mas isso é provavelmente um exagero. E você precisa estar preparado para repetir a operação em caso de falha de serialização.
Mais simples e rápido (e tão confiável com carga de gravação simultânea) é um bloqueio explícito no um linha a ser atualizada:
UPDATE tbl x
SET tbl_id = 24
, name = 'New Gal'
FROM (SELECT tbl_id, name FROM tbl WHERE tbl_id = 4 FOR UPDATE) y
WHERE x.tbl_id = y.tbl_id
RETURNING y.tbl_id AS old_id, y.name AS old_name
, x.tbl_id , x.name;
Observe como o
WHERE
condição movida para a subconsulta (novamente, pode ser qualquer coisa ), e apenas a auto-junção (em UNIQUE NOT NULL
column(s)) permanece na consulta externa. Isso garante que apenas as linhas bloqueadas pelo SELECT
interno são processados. O WHERE
condições podem resolver para um conjunto diferente de linhas um momento depois. Ver:
- Atomic UPDATE .. SELECT in Postgres
db<>mexa aqui
antigo sqlfiddle