sql >> Database >  >> RDS >> PostgreSQL

Postgres UPDATE met ORDER BY, hoe doe je dat?

Voor zover ik weet, is er geen manier om dit rechtstreeks te bereiken via de UPDATE uitspraak; de enige manier om de vergrendelingsvolgorde te garanderen, is door expliciet sloten te verwerven met een SELECT ... ORDER BY ID FOR UPDATE , bijv.:

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
)

Dit heeft het nadeel van het herhalen van de ID index opzoeken op de Balances tafel. In uw eenvoudige voorbeeld kunt u deze overhead vermijden door het fysieke rijadres op te halen (weergegeven door de ctid systeemkolom ) tijdens de vergrendelingsquery, en die gebruiken om de UPDATE . aan te sturen :

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

(Wees voorzichtig bij het gebruik van ctid s, aangezien de waarden van voorbijgaande aard zijn. We zijn hier veilig, omdat de sloten alle wijzigingen blokkeren.)

Helaas gebruikt de planner alleen de ctid in een beperkt aantal gevallen (u kunt zien of het werkt door te zoeken naar een "Tid Scan"-knooppunt in de EXPLAIN uitgang). Om meer gecompliceerde zoekopdrachten af ​​te handelen binnen één UPDATE verklaring, bijv. als uw nieuwe saldo werd geretourneerd door some_function() naast de ID moet je terugvallen op de ID-gebaseerde lookup:

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

Als de prestatieoverhead een probleem is, moet u een cursor gebruiken, die er ongeveer zo uitziet:

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. Ophalen van json-elementen met een specifieke sleutelnaam uit een complexe geneste structuur in postgres

  2. MySQL:hoe krijg ik alleen het gemiddelde van positieve waarden?

  3. Leg FOR uit in orakel

  4. Oct2014CPU crasht ArcGIS Desktop