sql >> Base de Datos >  >> RDS >> PostgreSQL

Devolver valores de columna previos a la ACTUALIZACIÓN usando solo SQL

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