Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Oracle DBMS - Lesen Sie eine Tabelle, bevor Sie die Aktualisierung in einem AFTER-Trigger verarbeiten - mutierende Tabelle

Nur zur Verdeutlichung:Die Mutating-Table-Ausnahme wird ausgelöst, weil Sie versuchen, aus den rooms zu lesen Tabelle in Ihrer Funktion, nicht weil Sie versuchen, aus den properties zu lesen Tisch. Da Sie einen Trigger auf Zeilenebene für rooms haben , das bedeutet, dass die rooms sich die Tabelle mitten in einer Änderung befindet, wenn der Trigger auf Zeilenebene ausgelöst wird, und dass sie sich möglicherweise in einem inkonsistenten Zustand befindet. Oracle hindert Sie daran, die rooms abzufragen Tabelle in dieser Situation, da die Ergebnisse nicht unbedingt deterministisch oder reproduzierbar sind.

Wenn Sie einen Trigger auf Anweisungsebene erstellt haben (Entfernen der FOR EACH ROW ) und Ihre Logik dort platzieren, würden Sie nicht mehr auf eine mutierende Tabellenausnahme stoßen, da die rooms Tabelle wäre nicht mehr in einem inkonsistenten Zustand. Ein Trigger auf Anweisungsebene kann jedoch nicht erkennen, welche Zeile(n) geändert wurden. Das würde bedeuten, dass Sie alle Eigenschaften durchsuchen müssten, um zu sehen, welche Statuswerte angepasst werden sollten. Das wird nicht besonders effizient sein.

Auf Kosten zusätzlicher Komplexität können Sie die Leistung verbessern, indem Sie erfassen, welche Eigenschaften sich in einem Trigger auf Zeilenebene geändert haben, und dann in einem Trigger auf Anweisungsebene darauf verweisen. Das erfordert im Allgemeinen drei Trigger und ein Paket, was die Anzahl der beweglichen Teile offensichtlich erheblich erhöht (wenn Sie auf 11.2 sind, können Sie einen zusammengesetzten Trigger mit drei Komponenten-Triggern verwenden, was die Dinge ein wenig vereinfacht, indem die Notwendigkeit entfällt, das Paket zu verwenden). . Das würde in etwa so aussehen

CREATE OR REPLACE PACKAGE trigger_collections
AS
  TYPE modified_property_tbl IS TABLE OF properties.property_id%type;
  g_modified_properties modified_property_tbl;
END;

-- Initialize the collection in a before statement trigger just in case
-- there were values there from a prior run
CREATE OR REPLACE TRIGGER trg_initialize_mod_prop_coll
  BEFORE INSERT OR UPDATE ON rooms
BEGIN
  trigger_collections.g_modified_properties := trigger_collections.modified_property_tbl();
END;

-- Put the property_id of the modified row in the collection
CREATE OR REPLACE TRIGGER trg_populate_mod_prop_coll
  AFTER INSERT OR UPDATE ON rooms
  FOR EACH ROW
BEGIN
  trigger_collections.g_modified_properties.extend();
  trigger_collections.g_modified_properties( trigger_collections.g_modified_properties.count + 1 ) := :new.property_id;
END;

CREATE OR REPLACE TRIGGER trg_process_mod_prop_coll
  AFTER INSERT OR UPDATE ON rooms
BEGIN
  FOR p IN 1 .. trigger_collections.g_modified_properties.count
  LOOP
    IF prop_vacancy_query( trigger_collections.g_modified_properties(i) ) = 0 
    THEN
      ...
END;