sql >> Databáze >  >> RDS >> Oracle

Oracle spoušť po vložení nebo odstranění

Narazili jste na klasickou výjimku „mutující tabulky“. Ve spouštěči ROW vám Oracle neumožňuje spouštět dotaz proti tabulce, na které je spouštěč definován – je to tedy SELECT proti TABLE1 v DELETING část spouštěče, která tento problém způsobuje.

Existuje několik způsobů, jak to obejít. V této situaci je možná nejlepší použít složený trigger, který by vypadal asi takto:

CREATE OR REPLACE TRIGGER TABLE1_NUM_TRG
  FOR INSERT OR DELETE ON TABLE1
COMPOUND TRIGGER
  TYPE NUMBER_TABLE IS TABLE OF NUMBER;
  tblTABLE2_IDS  NUMBER_TABLE;

  BEFORE STATEMENT IS
  BEGIN
    tblTABLE2_IDS := NUMBER_TABLE();
  END BEFORE STATEMENT;

  AFTER EACH ROW IS
  BEGIN
    IF INSERTING THEN
      UPDATE TABLE2 t2
        SET    t2.TABLE2NUM = :new.NUM
        WHERE  t2.ID = :new.TABLE2_ID;
    ELSIF DELETING THEN
      tblTABLE2_IDS.EXTEND;
      tblTABLE2_IDS(tblTABLE2_IDS.LAST) := :new.TABLE2_ID;
    END IF;
  END AFTER EACH ROW;

  AFTER STATEMENT IS
  BEGIN
    IF tblTABLE2_IDS.COUNT > 0 THEN
      FOR i IN tblTABLE2_IDS.FIRST..tblTABLE2_IDS.LAST LOOP
        UPDATE TABLE2 t2
          SET t2.TABLE2NUM = (SELECT NUM
                                FROM (SELECT t1.NUM
                                        FROM TABLE1 t1
                                        WHERE t1.TABLE2_ID = tblTABLE2_IDS(i) 
                                        ORDER BY modification_date DESC)
                                WHERE ROWNUM = 1)
          WHERE t2.ID = tblTABLE2_IDS(i);
      END LOOP;
    END IF;
  END AFTER STATEMENT;
END TABLE1_NUM_TRG;

Složený spouštěč umožňuje každý časový bod (BEFORE STATEMENT , BEFORE ROW , AFTER ROW a AFTER STATEMENT ), které mají být řešeny. Všimněte si, že časové body jsou vždy vyvolány v daném pořadí. Když je příslušný příkaz SQL (tj. INSERT INTO TABLE1 nebo DELETE FROM TABLE1 ) se provede a spustí se tento spouštěč, první časový bod, který bude vyvolán, bude BEFORE STATEMENT a kód v BEFORE STATEMENT handler alokuje PL/SQL tabulku pro uložení hromady čísel. V tomto případě čísla, která mají být uložena v tabulce PL/SQL, budou hodnoty TABLE2_ID z TABLE1. (Tabulka PL/SQL se používá místo například pole, protože tabulka může obsahovat různý počet hodnot, zatímco pokud bychom použili pole, museli bychom předem vědět, kolik čísel bychom museli uložit. Nemůžeme předem vědět, kolik řádků bude ovlivněno konkrétním příkazem, takže použijeme tabulku PL/SQL).

Když se zobrazí AFTER EACH ROW je dosaženo časového bodu a zjistíme, že zpracovávaný příkaz je INSERT, spouštěč prostě pokračuje a provede nezbytnou AKTUALIZACI TABULKY2, protože to nezpůsobí problém. Pokud se však provádí DELETE, spouštěč uloží TABLE1.TABLE2_ID do dříve přidělené tabulky PL/SQL. Když se zobrazí AFTER STATEMENT je konečně dosaženo časového bodu, je iterována dříve přidělená tabulka PL/SQL a pro každé nalezené TABLE2_ID je provedena příslušná aktualizace.

Dokumentace zde.



  1. SQLite SHOW TABLES Ekvivalent

  2. Ukládání raiserror zprávy SqlServeru v C#

  3. Jak vybrat první řádek pro každou skupinu v MySQL?

  4. 5 skvělých důvodů, proč si stáhnout a používat šablony Microsoft Access