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

ACTUALIZACIÓN de Postgres con ORDEN POR, ¿cómo hacerlo?

Hasta donde yo sé, no hay forma de lograr esto directamente a través de UPDATE declaración; la única forma de garantizar el orden de bloqueo es adquirir bloqueos explícitamente con SELECT ... ORDER BY ID FOR UPDATE , por ejemplo:

UPDATE Balances
SET Balance = 0
WHERE ID IN (
  SELECT ID FROM Balances
  WHERE ID IN (SELECT ID FROM some_function())
  ORDER BY ID
  FOR UPDATE
)

Esto tiene la desventaja de repetir el ID búsqueda de índice en los Balances mesa. En su ejemplo simple, puede evitar esta sobrecarga obteniendo la dirección de la fila física (representada por ctid columna del sistema ) durante la consulta de bloqueo, y usándolo para impulsar la UPDATE :

UPDATE Balances
SET Balance = 0
WHERE ctid = ANY(ARRAY(
  SELECT ctid FROM Balances
  WHERE ID IN (SELECT ID FROM some_function())
  ORDER BY ID
  FOR UPDATE
))

(Tenga cuidado al usar ctid s, ya que los valores son transitorios. Estamos a salvo aquí, ya que las cerraduras bloquearán cualquier cambio).

Desafortunadamente, el planificador solo utilizará el ctid en un conjunto limitado de casos (puede saber si está funcionando buscando un nodo "Tid Scan" en EXPLAIN producción). Para manejar consultas más complicadas dentro de una sola UPDATE declaración, por ej. si su nuevo saldo estaba siendo devuelto por some_function() junto con el ID, deberá recurrir a la búsqueda basada en ID:

UPDATE Balances
SET Balance = Locks.NewBalance
FROM (
  SELECT Balances.ID, some_function.NewBalance
  FROM Balances
  JOIN some_function() ON some_function.ID = Balances.ID
  ORDER BY Balances.ID
  FOR UPDATE
) Locks
WHERE Balances.ID = Locks.ID

Si la sobrecarga de rendimiento es un problema, deberá recurrir al uso de un cursor, que se vería así:

DO $$
DECLARE
  c CURSOR FOR
    SELECT Balances.ID, some_function.NewBalance
    FROM Balances
    JOIN some_function() ON some_function.ID = Balances.ID
    ORDER BY Balances.ID
    FOR UPDATE;
BEGIN
  FOR row IN c LOOP
    UPDATE Balances
    SET Balance = row.NewBalance
    WHERE CURRENT OF c;
  END LOOP;
END
$$