PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

AGGIORNAMENTO Postgres con ORDER BY, come si fa?

Per quanto ne so, non c'è modo di farlo direttamente tramite UPDATE dichiarazione; l'unico modo per garantire l'ordine dei blocchi è acquisire esplicitamente i blocchi con un SELECT ... ORDER BY ID FOR UPDATE , ad esempio:

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
)

Questo ha lo svantaggio di ripetere l'ID ricerca dell'indice su Balances tavolo. Nel tuo semplice esempio, puoi evitare questo sovraccarico recuperando l'indirizzo della riga fisica (rappresentato da ctid colonna di sistema ) durante la query di blocco e utilizzandolo per guidare l'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
))

(Fai attenzione quando usi ctid s, poiché i valori sono transitori. Siamo al sicuro qui, poiché i blocchi bloccheranno qualsiasi modifica.)

Sfortunatamente, il pianificatore utilizzerà solo il ctid in un ristretto insieme di casi (puoi sapere se funziona cercando un nodo "Tid Scan" in EXPLAIN produzione). Per gestire query più complicate all'interno di un singolo UPDATE dichiarazione, ad es. se il tuo nuovo saldo è stato restituito da some_function() oltre all'ID, dovrai tornare alla ricerca basata sull'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

Se l'overhead delle prestazioni è un problema, dovresti ricorrere all'utilizzo di un cursore, che sarebbe simile a questo:

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
$$