sql >> Database >  >> RDS >> PostgreSQL

Functie duurt eeuwig om uit te voeren voor een groot aantal records

Hoogstwaarschijnlijk loop je tegen race-omstandigheden . Wanneer u uw functie 1000 keer snel achter elkaar uitvoert in afzonderlijke transacties , gebeurt er iets als dit:

T1            T2            T3            ...
SELECT max(id) -- id 1
              SELECT max(id)  -- id 1
                            SELECT max(id)  -- id 1
                                          ...
              Row id 1 locked, wait ...
                            Row id 1 locked, wait ...
UPDATE id 1
                                          ... 

COMMIT
              Wake up, UPDATE id 1 again!
              COMMIT
                            Wake up, UPDATE id 1 again!
                            COMMIT
                                          ... 

Grotendeels herschreven en vereenvoudigd als SQL-functie:

CREATE OR REPLACE FUNCTION get_result(val1 text, val2 text)
  RETURNS text AS 
$func$
   UPDATE table t
   SET    id_used = 'Y'
        , col1 = val1
        , id_used_date = now() 
   FROM  (
      SELECT id
      FROM   table 
      WHERE  id_used IS NULL
      AND    id_type = val2
      ORDER  BY id
      LIMIT  1
      FOR    UPDATE   -- lock to avoid race condition! see below ...
      ) t1
   WHERE  t.id_type = val2
   -- AND    t.id_used IS NULL -- repeat condition (not if row is locked)
   AND    t.id = t1.id
   RETURNING  id;
$func$  LANGUAGE sql;

Gerelateerde vraag met veel meer uitleg:

Uitleggen

  • Voer geen twee afzonderlijke SQL-instructies uit. Dat is duurder en verlengt het tijdsbestek voor race-omstandigheden. Eén UPDATE met een subquery is veel beter.

  • U hebt geen PL/pgSQL nodig voor de eenvoudige taak. Je kunt nog gebruik PL/pgSQL, de UPDATE blijft hetzelfde.

  • Je moet de geselecteerde rij vergrendelen om je te verdedigen tegen race-omstandigheden. Maar u kunt dit niet doen met de aggregatiefunctie die u aanstuurt, omdat, per documentatie :

  • Vetgedrukte nadruk van mij. Gelukkig kun je min(id) . vervangen gemakkelijk met de equivalente ORDER BY / LIMIT 1 heb ik hierboven gegeven. Kan net zo goed een index gebruiken.

  • Als de tafel groot is, heb je nodig een index op id tenminste. Ervan uitgaande dat id is al geïndexeerd als PRIMARY KEY , dat zou helpen. Maar deze aanvullende gedeeltelijke index met meerdere kolommen zou waarschijnlijk veel meer helpen :

    CREATE INDEX foo_idx ON table (id_type, id)
    WHERE id_used IS NULL;
    

Alternatieve oplossingen

Adviessloten Misschien is dit de superieure benadering:

Of misschien wil je veel rijen tegelijk :




  1. Draaien met dynamische kolommen in Oracle

  2. Verbinding mislukt:toegang geweigerd voor gebruiker 'root'@'localhost' (met wachtwoord:JA) van php-functie

  3. Oracle-procedure retourneert geen resultaten bij het uitvoeren van een scripttaak op SSIS

  4. Pl/SQL- Krijg kolomnamen uit een query