Problema
El manual explica:
El RETURNING
opcional cláusula causa UPDATE
para calcular y devolver valores en función de cada fila realmente actualizada. Cualquier expresión que use las columnas de la tabla y/o columnas de otras tablas mencionadas en FROM
, se puede calcular. Se utilizan los nuevos valores (posteriores a la actualización) de las columnas de la tabla . La sintaxis de RETURNING
lista es idéntica a la de la lista de salida de SELECT
.
Énfasis en negrita mío. No hay forma de acceder a la fila anterior en un RETURNING
cláusula. Puede evitar esta restricción con un disparador o un SELECT
separado antes el UPDATE
envuelto en una transacción o envuelto en un CTE como se comentó.
Sin embargo, lo que está tratando de lograr funciona perfectamente bien si te unes a otra instancia de la tabla en el FROM
cláusula:
Solución sin escrituras 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;
Devoluciones:
old_id | old_name | tbl_id | name
--------+----------+--------+---------
3 | Old Guy | 23 | New Guy
La(s) columna(s) usada(s) para la autounión debe ser UNIQUE NOT NULL
. En el ejemplo simple, WHERE
la condición está en la misma columna tbl_id
, pero eso es solo una coincidencia. Funciona para cualquier condiciones.
Probé esto con las versiones de PostgreSQL de la 8.4 a la 13.
Es diferente para INSERT
:
- INSERTAR EN... DESDE SELECCIONAR... DEVOLVER asignaciones de id
Soluciones con carga de escritura simultánea
Hay varias formas de evitar condiciones de carrera con operaciones de escritura simultáneas en las mismas filas. (Tenga en cuenta que las operaciones de escritura simultáneas en filas no relacionadas no son ningún problema). El método simple, lento y seguro (pero costoso) es ejecutar la transacción con SERIALIZABLE
nivel de aislamiento:
BEGIN ISOLATION LEVEL SERIALIZABLE;
UPDATE ... ;
COMMIT;
Pero eso es probablemente una exageración. Y debe estar preparado para repetir la operación en caso de que falle la serialización.
Más simple y más rápido (e igual de confiable con la carga de escritura simultánea) es un bloqueo explícito en el uno fila a actualizar:
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;
Tenga en cuenta cómo WHERE
condición movida a la subconsulta (de nuevo, puede ser cualquier cosa ), y solo la unión automática (en UNIQUE NOT NULL
columna(s)) permanece en la consulta externa. Esto garantiza que solo las filas bloqueadas por el SELECT
interno son procesados. El WHERE
las condiciones pueden resolverse en un conjunto diferente de filas un momento después.
Ver:
- ACTUALIZACIÓN atómica... SELECCIONAR en Postgres
db<>violín aquí
Sqlfiddle antiguo