Bara för att förtydliga, mutationstabellundantaget kastas eftersom du försöker läsa från rooms
tabell i din funktion, inte för att du försöker läsa från properties
tabell. Eftersom du har en utlösare på radnivå på rooms
, det betyder att rooms
tabellen är mitt i en förändring när utlösaren på radnivå aktiveras och att den kan vara i ett inkonsekvent tillstånd. Oracle hindrar dig från att fråga rooms
tabell i den situationen eftersom resultaten inte nödvändigtvis är deterministiska eller reproducerbara.
Om du skapade en utlösare på satsnivå (att ta bort FOR EACH ROW
) och lägg din logik där, skulle du inte längre stöta på ett mutationstabellundantag eftersom rooms
tabellen inte längre skulle vara i ett inkonsekvent tillstånd. En utlösare på satsnivå kan dock inte se vilken eller vilka rader som har ändrats. Det skulle innebära att du måste titta över alla egenskaper för att se vilka statusvärden som bör justeras. Det kommer inte att vara särskilt effektivt.
Till priset av ytterligare komplexitet kan du förbättra prestandan genom att fånga vilka egenskaper som ändrats i en utlösare på radnivå och sedan hänvisa till det i en utlösare på uttalandenivå. Det kräver i allmänhet tre triggers och ett paket, vilket uppenbarligen ökar antalet rörliga bitar avsevärt (om du är på 11.2 kan du använda en sammansatt trigger med tre komponentutlösare som förenklar saker och ting lite genom att eliminera behovet av att använda paketet) . Det skulle se ut ungefär som
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;