📜 ⬆️ ⬇️

All about triggers in Oracle

Traditionally, the article is written thesis. More detailed content can be found in the video attached at the bottom of the article recording a lecture on Oracle triggers.


Trigger Overview


A trigger is a pl / sql named block that is stored in a database.


Trigger classification:


Why use triggers:

')


where plsql_trigger_source is such a construction:


Constructs simple_dml_trigger, instead_of_dml_trigger, compound_dml_trigger and system_trigger will be given in the relevant sections of the article.

Dml triggers




The simple_dml_trigger construction :

Where, dml_event_clause:

referencing_clause:

trigger_edition_clause:

trigger_body:


By anchored object are divided into:


On launch events:


By scope:


By response time:


Crossedition triggers - used for inter-editing interaction, for example, for transferring and transforming data from fields that are not in the new edition to other fields.

Conditional predicates to determine the operation on which the trigger worked:
PredicateDescription
InsertingTrue if trigger triggered on Insert operation
UpdatingTrue if trigger triggered on Update operation
Updating ('colum')True if the trigger was triggered by an Update operation that affects a specific field.
DeletingTrue if trigger triggered on Delete operation


These predicates can be used anywhere Boolean expressions can be used.

Example
CREATE OR REPLACE TRIGGER t BEFORE INSERT OR UPDATE OF salary, department_id OR DELETE ON employees BEGIN CASE WHEN INSERTING THEN DBMS_OUTPUT.PUT_LINE('Inserting'); WHEN UPDATING('salary') THEN DBMS_OUTPUT.PUT_LINE('Updating salary'); WHEN UPDATING('department_id') THEN DBMS_OUTPUT.PUT_LINE('Updating department ID'); WHEN DELETING THEN DBMS_OUTPUT.PUT_LINE('Deleting'); END CASE; END; 



Pseudo recordings


There are pseudo-entries that allow you to refer to the fields of a variable record and get the field values ​​before the change and the field values ​​after the change. These are old and new entries. Using the Referencing construct, you can change their names. The structure of these entries is tablename% rowtype. These entries are only for row level triggers or compound triggers (with sections of the recording level).
Trigger OperationOLD.columnNEW.column
InsertNullNew value
UpdateOld meaningNew value
DeleteOld meaningNull


Restrictions:


Instead of dml triggers




Construction instead_of_dml_trigger:




Example
 CREATE OR REPLACE VIEW order_info AS SELECT c.customer_id, c.cust_last_name, c.cust_first_name, o.order_id, o.order_date, o.order_status FROM customers c, orders o WHERE c.customer_id = o.customer_id; CREATE OR REPLACE TRIGGER order_info_insert INSTEAD OF INSERT ON order_info DECLARE duplicate_info EXCEPTION; PRAGMA EXCEPTION_INIT (duplicate_info, -00001); BEGIN INSERT INTO customers (customer_id, cust_last_name, cust_first_name) VALUES ( :new.customer_id, :new.cust_last_name, :new.cust_first_name); INSERT INTO orders (order_id, order_date, customer_id) VALUES ( :new.order_id, :new.order_date, :new.customer_id); EXCEPTION WHEN duplicate_info THEN RAISE_APPLICATION_ERROR ( num=> -20107, msg=> 'Duplicate customer or order ID'); END order_info_insert; 



Instead of triggers on Nested Table Columns of Views


You can create a trigger for a nested table view. In such a trigger, there is also an additional pseudo-record - parent, which refers to the entire record of the view (standard pseudo-records old and new refer only to the records of the nested table)

An example of such a trigger
 -- Create type of nested table element: CREATE OR REPLACE TYPE nte AUTHID DEFINER IS OBJECT ( emp_id NUMBER(6), lastname VARCHAR2(25), job VARCHAR2(10), sal NUMBER(8,2) ); / -- Created type of nested table: CREATE OR REPLACE TYPE emp_list_ IS TABLE OF nte; / -- Create view: CREATE OR REPLACE VIEW dept_view AS SELECT d.department_id, d.department_name, CAST (MULTISET (SELECT e.employee_id, e.last_name, e.job_id, e.salary FROM employees e WHERE e.department_id = d.department_id ) AS emp_list_ ) emplist FROM departments d; -- Create trigger: CREATE OR REPLACE TRIGGER dept_emplist_tr INSTEAD OF INSERT ON NESTED TABLE emplist OF dept_view REFERENCING NEW AS Employee PARENT AS Department FOR EACH ROW BEGIN -- Insert on nested table translates to insert on base table: INSERT INTO employees ( employee_id, last_name, email, hire_date, job_id, salary, department_id ) VALUES ( :Employee.emp_id, -- employee_id :Employee.lastname, -- last_name :Employee.lastname || '@company.com', -- email SYSDATE, -- hire_date :Employee.job, -- job_id :Employee.sal, -- salary :Department.department_id -- department_id ); END; 



Triggers a trigger statement insert
 INSERT INTO TABLE ( SELECT d.emplist FROM dept_view d WHERE department_id = 10 ) VALUES (1001, 'Glenn', 'AC_MGR', 10000); 



Compound DML triggers (compound DML triggers)


Appeared in version 11G, these triggers include processing of all kinds of DML triggers in one block.
Construction compound_dml_trigger:


Where, compound_trigger_block:


timing_point_section:


timing_point:


tps_body:



Mainly used to:


Composite trigger structure


May contain variables that live throughout the execution of the statement that caused the trigger to fire.
This trigger contains the following sections:

In these triggers there is no initialization section, but for these purposes you can use the section before statement.
If there is neither a section before statement, nor a section after statement in the trigger, and the operator does not affect any records, this trigger does not work.

Restrictions:


Example
 create or replace trigger tr_table_test_compound for update or delete or insert on table_test compound trigger v_count pls_integer := 0; before statement is begin dbms_output.put_line ( 'before statement' ); end before statement; before each row is begin dbms_output.put_line ( 'before insert' ); end before each row; after each row is begin dbms_output.put_line ( 'after insert' ); v_count := v_count + 1; end after each row; after statement is begin dbms_output.put_line ( 'after statement' ); end after statement; end tr_table_test_compound; 



Basic rules for determining DML triggers




Limitations of DML Triggers




Error mutation table ORA-04091



If you try to get or change data in the target table in a row-level trigger, then Oracle will not allow this and throws an ORA-04091 error. The TABLE_TEST table changes, the trigger / function may not notice it.
To work around this problem, use the following techniques:


System triggers (System triggers)


System_trigger construction :

Such triggers apply to either the schema or the entire database.

There are several options at which point in time the system trigger is triggered:


Schema level triggers




Trigger example
 CREATE OR REPLACE TRIGGER drop_trigger BEFORE DROP ON hr.SCHEMA BEGIN RAISE_APPLICATION_ERROR ( num => -20000, msg => 'Cannot drop object'); END; 



Database Triggers (database triggers)




Trigger example
 CREATE OR REPLACE TRIGGER check_user AFTER LOGON ON DATABASE BEGIN check_user; EXCEPTION WHEN OTHERS THEN RAISE_APPLICATION_ERROR (-20000, 'Unexpected error: '|| DBMS_Utility.Format_Error_Stack); END; 



Instead of create triggers




Trigger example
 CREATE OR REPLACE TRIGGER t INSTEAD OF CREATE ON SCHEMA BEGIN EXECUTE IMMEDIATE 'CREATE TABLE T (n NUMBER, m NUMBER)'; END; 



Attributes of system triggers


AttributeReturn value and type
ora_client_ip_addressVararch2
client ip address
Example:
 IF (ora_sysevent = 'LOGON') THEN v_addr := ora_client_ip_address; END IF; 
ora_database_nameVarchar2 (50)
database name
Example:
 v_db_name := ora_database_name; 
ora_des_encrypted_passwordVararch2
DES-encrypted user password that is created or modified.
Example:
 IF (ora_dict_obj_type = 'USER') THEN INSERT INTO event_table VALUES (ora_des_encrypted_password); END IF; 
ora_dict_obj_nameVarchar2 (30)
the name of the object on which the DDL operation is performed

Example:
 INSERT INTO event_table VALUES ('Changed object is ' || ora_dict_obj_name); 
ora_dict_obj_name_list (
name_list OUT ora_name_list_t
)
Pls_integer
number of objects changed by the command
Name_list - list of objects changed by the command

Example:
 IF (ora_sysevent='ASSOCIATE STATISTICS') THEN number_modified := ora_dict_obj_name_list(name_list); END IF; 
ora_dict_obj_ownerVarchar2 (30)
the owner of the object on which the DDL operation is performed

Example:
 INSERT INTO event_table VALUES ('object owner is' || ora_dict_obj_owner); 
ora_dict_obj_owner_list (
owner_list OUT ora_name_list_t
)
Pls_integer
number of owners of objects changed by the team
Owner_list - the list of owners of the objects changed by the command

Example:
 IF (ora_sysevent='ASSOCIATE STATISTICS') THEN number_modified := ora_dict_obj_name_list(owner_list); END IF; 
ora_dict_obj_typeVarchar2 (20)
the type of the object on which the ddl operation is performed

Example:
 INSERT INTO event_table VALUES ('This object is a ' || ora_dict_obj_type); 
ora_grantee (
user_list OUT ora_name_list_t
)
Pls_integer
number of users involved in the grant operation
User_list is a list of these users.

Example:
 IF (ora_sysevent = 'GRANT') THEN number_of_grantees := ora_grantee(user_list); END IF; 
ora_instance_numNumber
instance number

Example:
 IF (ora_instance_num = 1) THEN INSERT INTO event_table VALUES ('1'); END IF; 
ora_is_alter_column (
column_name IN VARCHAR2
)
Boolean
True if the specified field was changed by the alter operation. Otherwise false

Example:
 IF (ora_sysevent = 'ALTER' AND ora_dict_obj_type = 'TABLE') THEN alter_column := ora_is_alter_column('C'); END IF; 
ora_is_creating_nested_tableBoolean
true if the current event is the creation of a nested table. Otherwise false

Example:
 IF (ora_sysevent = 'CREATE' AND ora_dict_obj_type = 'TABLE' AND ora_is_creating_nested_table) THEN INSERT INTO event_table VALUES ('A nested table is created'); END IF; 
ora_is_drop_column (
column_name IN VARCHAR2
)
Boolean
true if the specified field is deleted. Otherwise false

Example:
 IF (ora_sysevent = 'ALTER' AND ora_dict_obj_type = 'TABLE') THEN drop_column := ora_is_drop_column('C'); END IF; 
ora_is_servererror (
error_number IN VARCHAR2
)
Boolean
true if an exception has been thrown with error_number number. Otherwise false

Example:
 IF ora_is_servererror(error_number) THEN INSERT INTO event_table VALUES ('Server error!!'); END IF; 
ora_login_userVarchar2 (30)
current user name

Example:
 SELECT ora_login_user FROM DUAL; 
ora_partition_posPls_integer
in instead of trigger for create table position in the text of the sql command, where the partition construction can be inserted

Example:
 -- Retrieve ora_sql_txt into sql_text variable v_n := ora_partition_pos; v_new_stmt := SUBSTR(sql_text,1,v_n - 1) || ' ' || my_partition_clause || ' ' || SUBSTR(sql_text, v_n)); 
ora_privilege_list (
privilege_list OUT ora_name_list_t
)
Pls_integer
the number of privileges involved in a grant or revoke operation
Privilege_list - a list of these privileges

Example:
 IF (ora_sysevent = 'GRANT' OR ora_sysevent = 'REVOKE') THEN number_of_privileges := ora_privilege_list(privilege_list); END IF; 
ora_revokee (
user_list OUT ora_name_list_t
)
Pls_integer
the number of users involved in the revoke operation
User_list is a list of these users.

Example:
 IF (ora_sysevent = 'REVOKE') THEN number_of_users := ora_revokee(user_list); END IF; 
ora_server_error (
position IN PLS_INTEGER
)
Number
error code in the specified position error stack, where 1 is the top of the stack

Example:
 INSERT INTO event_table VALUES ('top stack error ' || ora_server_error(1)); 
ora_server_error_depthPls_integer
number of error messages in error stack

Example:
 n := ora_server_error_depth; -- Use n with functions such as ora_server_error 
ora_server_error_msg (
position IN PLS_INTEGER
)
Vararch2
error message in the specified place error stack

Example:
 INSERT INTO event_table VALUES ('top stack error message' || ora_server_error_msg(1)); 
ora_server_error_num_params (
position IN PLS_INTEGER
)
Pls_integer
the number of rows replaced (using the% s format) in the specified position error stack

Example:
 n := ora_server_error_num_params(1); 
ora_server_error_param (
position IN PLS_INTEGER,
param IN PLS_INTEGER
)
Vararch2
the replaced text in the error message in the specified position error stack (the param on the account returns the replaced text)

Example:
 -- Second %s in "Expected %s, found %s": param := ora_server_error_param(1,2); 
ora_sql_txt (
sql_text OUT ora_name_list_t
)
Pls_integer
the number of items in the pl / sql collection in the sql_text.
The parameter sql_text itself returns the command text for which the trigger worked.

Example:
 CREATE TABLE event_table (col VARCHAR2(2030)); DECLARE sql_text ora_name_list_t; n PLS_INTEGER; v_stmt VARCHAR2(2000); BEGIN n := ora_sql_txt(sql_text); FOR i IN 1..n LOOP v_stmt := v_stmt || sql_text(i); END LOOP; INSERT INTO event_table VALUES ('text of triggering statement: ' || v_stmt); END; 
ora_syseventVarchar2 (20)
the name of the command that triggers

Example:
 INSERT INTO event_table VALUES (ora_sysevent); 
ora_with_grant_optionBoolean
true if privileges are granted with a grant option. Otherwise false.

Example:
 IF (ora_sysevent = 'GRANT' AND ora_with_grant_option = TRUE) THEN INSERT INTO event_table VALUES ('with grant option'); END IF; 
ora_space_error_info (
error_number OUT NUMBER
error_type OUT VARCHAR2,
object_owner out VARCHAR2,
table_space_name OUT VARCHAR2,
object_name OUT VARCHAR2,
sub_object_name OUT VARCHAR2
)
Boolean
true if the error occurs due to lack of space. In the output parameters of the object.

Example:
 IF (ora_space_error_info ( eno,typ,owner,ts,obj,subobj) = TRUE) THEN DBMS_OUTPUT.PUT_LINE('The object '|| obj || ' owned by ' || owner || ' has run out of space.'); END IF; 


System Trigger Events


EventDescriptionAvailable Attributes
AFTER STARTUPWhen starting the database. It happens only the database level. When an error is written to the system log.ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
BEFORE SHUTDOWNBefore the server starts the shutdown process. It happens only the database level. When an error is written to the system log.ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
AFTER DB_ROLE_CHANGEWhen you start the database for the first time after changing roles from standby to primary or from primary to primary to standby.
it is used only in the Data Guard configuration; there is only a database level.
ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
AFTER SERVERERORIf any error occurs (if with a condition, then it only works for the error specified in the condition). When an error occurs in the body of a trigger, it does not call itself recursively.ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_server_error
ora_is_servererror
ora_space_error_info
BEFORE ALTER

AFTER ALTER
If the object is changed by the alter commandora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_dict_obj_type
ora_dict_obj_name
ora_dict_obj_owner
ora_des_encrypted_password
(for ALTER USER events)
ora_is_alter_column
(for ALTER TABLE events)
ora_is_drop_column
(for ALTER TABLE events)
BEFORE DROP

AFTER DROP
When deleting an objectora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_dict_obj_type
ora_dict_obj_name
ora_dict_obj_owner
BEFORE ANALYZE

AFTER ANALYZE
When the analyze command is triggeredora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_dict_obj_name
ora_dict_obj_type
ora_dict_obj_owner
BEFORE ASSOCIATE STATISTICS

AFTER ASSOCIATE STATISTICS
When executing associate statisticsora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_dict_obj_name
ora_dict_obj_type
ora_dict_obj_owner
ora_dict_obj_name_list
ora_dict_obj_owner_list
BEFORE AUDIT

AFTER AUDIT

BEFORE NOAUDIT

AFTER NOAUDIT
When executing an audit or noaudit commandora_sysevent
ora_login_user
ora_instance_num
ora_database_name
BEFORE COMMENT

AFTER COMMENT
When adding a comment to an objectora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_dict_obj_name
ora_dict_obj_type
ora_dict_obj_owner
BEFORE CREATE

AFTER CREATE
When creating an objectora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_dict_obj_type
ora_dict_obj_name
ora_dict_obj_owner
ora_is_creating_nested_table
(for CREATE TABLE events)
BEFORE DDL

AFTER DDL
It works on most DDL commands, except: alter database, create control file, create database.ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_dict_obj_name
ora_dict_obj_type
ora_dict_obj_owner
BEFORE DISASSOCIATE STATISTICS

AFTER DISASSOCIATE STATISTICS
When you run the disassociate statistics commandora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_dict_obj_name
ora_dict_obj_type
ora_dict_obj_owner
ora_dict_obj_name_list
ora_dict_obj_owner_list
BEFORE GRANT

AFTER GRANT
When executing the grant commandora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_dict_obj_name
ora_dict_obj_type
ora_dict_obj_owner
ora_grantee
ora_with_grant_option
ora_privilege_list
BEFORE LOGOFFIt triggers before disconnecting a user, it can be a schema or database levelora_sysevent
ora_login_user
ora_instance_num
ora_database_name
AFTER LOGONIt works after the user has successfully established a connection to the database. When an error prohibits the user from entering Does not affect SYS.ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_client_ip_address
BEFORE RENAME

AFTER RENAME
When executing the rename commandora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_dict_obj_name
ora_dict_obj_owner
ora_dict_obj_type
BEFORE REVOKE

AFTER REVOKE
With the revoke commandora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_dict_obj_name
ora_dict_obj_type
ora_dict_obj_owner
ora_revokee
ora_privilege_list
AFTER SUSPENDIt works if the sql command is suspended due to a server error (out of memory).
In this case, the trigger must change the conditions so that the command execution is resumed)
ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_server_error
ora_is_servererror
ora_space_error_info
BEFORE TRUNCATE

AFTER TRUNCATE
When executing the truncate commandora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_dict_obj_name
ora_dict_obj_type
ora_dict_obj_owner


Compiling triggers


If an error occurs during the execution of the create trigger command, the trigger will still be created, but will be in an invalid state. In this case, all attempts to perform an operation (on which the trigger should fire) on the object on which such a trigger hangs, will end with an error. This does not apply to cases where:

To recompile a trigger, use the alter trigger command.

Trigger Exceptions


In the event that an exception occurs in the trigger, the entire operation is rolled back (including any changes made to the inside of the trigger). Exceptions to this:


The order of execution of triggers


The trigger_ordering_clause construction :


  1. First, all the statement before the trigger is executed.
  2. Then all before each row of the trigger
  3. After all after each row of the trigger
  4. And at the end all after statement trigger

To set explicitly the order of execution of triggers that fire at the same point in time (because such an order is not defined by default), use the following and precedes constructs.

Enable / Disable Triggers


This may be necessary, for example, to load a large amount of information into a table.
To enable / disable the trigger, use the command:
 ALTER TRIGGER [schema.]trigger_name { ENABLE | DISABLE }; 

To enable / disable all triggers on the table at once:
 ALTER TABLE table_name { ENABLE | DISABLE } ALL TRIGGERS; 


To change a trigger, you can either use the Create or replace trigger command, or delete the drop trigger first, and then create the create trigger again.
The alter trigger operation only allows you to enable / disable a trigger, compile it or rename it.
Trigger compilation:
 alter trigger TRIGGER_NAME compile; 


Rights for operations with triggers



To work with triggers, even in its scheme, the create trigger privilege is necessary, it gives the rights to create, modify and delete.
 grant create trigger to USER; 

To work with triggers in all other schemes, you need the * any trigger privilege. Please note that the rights are given separately to create, modify and delete.
 grant create any trigger to USER; grant alter any trigger to USER; grant drop any trigger to USER; 

To work with DATABASE level system triggers, ADMINISTER DATABASE TRIGGER privilege is required.
 grant ADMINISTER DATABASE TRIGGER to USER; 


Triggers data dictionaries:




A video recording of the lecture, based on which this article was written:



Many other Oracle-related videos can be found on this channel: www.youtube.com/c/MoscowDevelopmentTeam

Other Oracle Articles


All about collections in Oracle

Source: https://habr.com/ru/post/256655/


All Articles