Oracle入门(十四.22)之创建DDL和数据库事件触发器
发布日期:2021-07-01 01:37:03 浏览次数:2 分类:技术文章

本文共 4646 字,大约阅读时间需要 15 分钟。

一、什么是DDL和数据库事件触发器?

DDL语句触发DDL触发器:CREATE,ALTER或DROP。
数据库事件触发器由数据库中的非SQL事件触发,例如:
•用户连接到数据库或与数据库断开连接。
•DBA启动或关闭数据库。

•用户会话中引发了特定的异常。

(1)在DDL语句中创建触发器语法

•ON DATABASE在数据库中的所有模式上触发DDL

•ON SCHEMA仅针对您自己的模式中的对象触发DDL

CREATE [OR REPLACE] TRIGGER trigger_nameTiming[ddl_event1 [OR ddl_event2 OR ...]]ON {DATABASE|SCHEMA}trigger_body

(2)DDL触发器的示例

每次在模式中创建新的数据库对象时,都希望写入日志记录:

CREATE OR REPLACE TRIGGER log_create_triggAFTER CREATE ON SCHEMABEGIN INSERT INTO log_table VALUES (USER, SYSDATE);END;
只要有任何(类型)的对象被创建,触发器就会触发。 您不能创建引用特定数据库对象的DDL触发器。

(3)DDL触发器的第二个例子

防止从模式中删除任何对象。

CREATE OR REPLACE TRIGGER prevent_drop_triggBEFORE DROP ON SCHEMABEGIN RAISE_APPLICATION_ERROR (-20203, 'Attempted drop – failed');END;

只要有任何(类型)的对象被删除,触发器就会触发。 同样,您不能创建引用特定数据库对象的DDL触发器。

(4)在数据库事件语法上创建触发器

•ON DATABASE触发数据库中所有会话的事件触发器。
•ON SCHEMA仅为您自己的会话触发触发器。
CREATE [OR REPLACE] TRIGGER trigger_nametiming[database_event1 [OR database_event2 OR ...]]ON {DATABASE|SCHEMA}trigger_body

二、LOGON、LOGOFF和SERVERERROR

示例1:LOGON和LOGOFF触发器

CREATE OR REPLACE TRIGGER logon_trigAFTER LOGON ON SCHEMABEGININSERT INTO log_trig_table(user_id,log_date,action) VALUES (USER, SYSDATE, 'Logging on');END;

CREATE OR REPLACE TRIGGER logoff_trigBEFORE LOGOFF ON SCHEMABEGININSERT INTO log_trig_table(user_id,log_date,action) VALUES (USER, SYSDATE, 'Logging off');END;

示例2:SERVERERROR触发器

想保留会话中发生的任何ORA-00942错误的日志:

CREATE OR REPLACE TRIGGER servererror_trigAFTER SERVERERROR ON SCHEMABEGINIF (IS_SERVERERROR (942)) THEN INSERT INTO error_log_table ...END IF;END;

三、触发器中的CALL语句

没有结束;语句,并且在CALL语句结尾处没有分号。

语法:

CREATE [OR REPLACE] TRIGGER trigger_nametimingevent1 [OR event2 OR event3]ON table_name[REFERENCING OLD AS old | NEW AS new][FOR EACH ROW][WHEN condition]CALL procedure_name

例子:

CREATE OR REPLACE TRIGGER log_employeeBEFORE INSERT ON EMPLOYEES CALL log_execution

四、突变表和行触发器

    突变表是一个当前正在由DML语句修改的表。

    行触发器不能从变异表中选择,因为它会看到不一致的数据集(当触发器尝试读取数据时,表中的数据将会改变)。 但是,如果需要,行触发器可以从不同的表中进行选择。

此限制不适用于DML语句触发器,仅适用于DML行触发器。

突变表:例子

CREATE OR REPLACE TRIGGER check_salary BEFORE INSERT OR UPDATE OF salary, job_id ON employees FOR EACH ROWDECLARE v_minsalary employees.salary%TYPE; v_maxsalary employees.salary%TYPE;BEGIN SELECT MIN(salary), MAX(salary)     INTO v_minsalary, v_maxsalary     FROM employees WHERE job_id = :NEW.job_id; IF :NEW.salary < v_minsalary OR     :NEW.salary > v_maxsalary THEN     RAISE_APPLICATION_ERROR(-20505,'Out of range'); END IF;END;
UPDATE employeesSET salary = 3400WHERE last_name = 'Davies';
出错:
ORA-04091: table USVA_TEST_SQL01_T01_EMPLOYEES is mutating,trigger/function may notsee itORA-06512: at “USVA_TEST_SQL01_T01.CHECK_SALARY”, line 5ORA-04088: error during execution of trigger‘USVA_TEST_SQL01_T01.CHECK_SALARY’3. WHERE last_name – ‘Davies’;

五、触发器的更多可能用途

    不应该创建触发器来执行某些可以通过其他方式轻松完成的操作,例如通过检查约束或适当的对象权限。 但是有时你必须创建一个触发器,因为没有其他方法可以做需要的事情。
    以下示例只显示了必须创建触发器的三种情况。 还有更多!

(1)第一个例子

数据库安全性(谁可以做什么)通常由系统和对象权限控制。 例如,用户SCOTT需要更新EMPLOYEES行:

GRANT UPDATE ON employees TO scott;

但是,SCOTT被允许这样做时,单凭权限无法控制。 为此,我们需要一个触发器:

CREATE OR REPLACE TRIGGER weekdays_emp BEFORE UPDATE ON employeesBEGINIF (TO_CHAR (SYSDATE, 'DY') IN ('SAT','SUN')) THEN RAISE_APPLICATION_ERROR(-20506,'You may only change data during normal business hours.');END IF;END;

(2)第二个例子

数据库完整性(允许DML)通常由约束条件控制。 例如,每个员工的工资必须至少为500美元:

ALTER TABLE employees ADD CONSTRAINT ck_salary CHECK (salary >= 500);

如果一条业务规则指出员工的薪水可以提高但不降低,这个限制并不能阻止员工的薪水从700美元降低到600美元。 为此,我们需要一个行触发器。此代码显示在下一张幻灯片中。

现在我们不再需要约束了。

CREATE OR REPLACE TRIGGER check_salary BEFORE UPDATE OF salary ON employees FOR EACH ROW WHEN (NEW.salary < OLD.salary OR NEW.salary < 500)BEGIN RAISE_APPLICATION_ERROR (-20508, 'Do not decrease salary.');END;

(3)第三个例子

您需要创建一个显示部门总工资单的报表。 你可以声明和使用这个游标:

...CURSOR tot_sals IS SELECT SUM(salary) FROM employees WHERE department_id = p_dept_id;...
但是,如果在一个大型组织中,该部门有10,000名员工呢? 从EMPLOYEES表中抽取10,000行可能太慢。 下面展示了一个更快的方法来做到这一点。

首先,我们在DEPARTMENTS表中添加一个新列以存储每个部门的总工资单:

ALTER TABLE DEPARTMENTS ADD (total_salary NUMBER(12,2));

接下来,只填写当前总工资单的这一栏:

UPDATE departments d SET total_salary = (SELECT SUM(salary) FROM employees WHERE department_id = d.department_id);

现在,我们必须在更改工资时保持这一新列。 这是通过使用DML行触发器完成的。

CREATE OR REPLACE PROCEDURE increment_salary (p_id IN NUMBER, p_new_sal IN NUMBER) ISBEGIN UPDATE departments SET total_salary = total_salary + NVL(p_new_sal,0) WHERE department_id = p_id;END increment_salary;
CREATE OR REPLACE TRIGGER compute_salaryAFTER INSERT OR UPDATE OF salary OR DELETEON employees FOR EACH ROWBEGINIF DELETING THEN increment_salary (:OLD.department_id,(:OLD.salary * -1));ELSIF UPDATING THEN increment_salary (:NEW.department_id,(:NEW.salary - :OLD.salary));ELSE increment_salary (:NEW.department_id,:NEW.salary);END IF;END;

转载地址:https://maokun.blog.csdn.net/article/details/80536346 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Oracle入门(十四.23)之管理触发器
下一篇:Oracle入门(十四.21)之创建DML触发器:第二部分

发表评论

最新留言

很好
[***.229.124.182]2024年04月18日 06时14分53秒