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

Postgres OPDATERING med ORDER BY, hvordan gør man det?

Så vidt jeg ved, er der ingen måde at opnå dette direkte gennem UPDATE udmelding; den eneste måde at garantere låserækkefølge på er eksplicit at erhverve låse med en SELECT ... ORDER BY ID FOR UPDATE , f.eks.:

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
)

Dette har ulempen ved at gentage ID indeksopslag på Balances bord. I dit simple eksempel kan du undgå denne overhead ved at hente den fysiske rækkeadresse (repræsenteret ved ctid systemkolonne ) under låseforespørgslen og bruge den til at køre 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
))

(Vær forsigtig, når du bruger ctid s, da værdierne er forbigående. Vi er i sikkerhed her, da låsene vil blokere eventuelle ændringer.)

Desværre vil planlæggeren kun bruge ctid i et snævert sæt tilfælde (du kan se, om det virker, ved at kigge efter en "Tid Scan"-node i EXPLAIN produktion). At håndtere mere komplicerede forespørgsler inden for en enkelt UPDATE erklæring, f.eks. hvis din nye saldo blev returneret af some_function() ved siden af ​​ID'et skal du vende tilbage til det ID-baserede opslag:

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

Hvis ydelsesomkostningerne er et problem, skal du ty til at bruge en markør, som ville se nogenlunde sådan ud:

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


  1. SQL-kommandoer er ikke kompatible af H2

  2. Brug af ODBC med Salesforce og Okta Single Sign On (SSO)

  3. Sletning af alle poster i en tabel, der ikke er refereret til fra en anden tabel

  4. Javascript funktion post og kalde php script