触发器trigger的使用.docx
《触发器trigger的使用.docx》由会员分享,可在线阅读,更多相关《触发器trigger的使用.docx(21页珍藏版)》请在冰豆网上搜索。
触发器trigger的使用
触发器的使用
1触发器2
1.1数据库领域名词2
1.2创建触发器的SQL语法2
1.3触发器的优点2
1.4比较触发器与约束3
1.5慎用触发器4
1.6触发器命名规则4
2触发器应用4
2.1功能5
2.2触发器的组成部分5
2.2.1触发器名称5
2.2.2触发语句5
2.3触发器类型6
2.3.1语句触发器6
2.3.2行触发器8
2.3.3INSTEADOF触发器更新视图11
2.3.4模式触发器12
2.3.5数据库触发器13
2.4禁用和启用触发器16
1触发器
资料来源:
《
触发器(trigger)是个特殊的存储过程,它的执行不是由程序调用,也不是手工启动,而是由事件来触发,比如当对一个表进行操作(insert,delete,update)时就会激活它执行。
触发器经常用于加强数据的完整性约束和业务规则等。
触发器可以从DBA_TRIGGERS,USER_TRIGGERS数据字典中查到。
1.1数据库领域名词
触发器可以查询其他表,而且可以包含复杂的SQL语句。
它们主要用于强制服从复杂的业务规则或要求。
例如:
您可以根据客户当前的帐户状态,控制是否允许插入新订单。
触发器也可用于强制引用完整性,以便在多个表中添加、更新或删除行时,保留在这些表之间所定义的关系。
然而,强制引用完整性的最好方法是在相关表中定义主键和外键约束。
如果使用数据库关系图,则可以在表之间创建关系以自动创建外键约束。
1.2创建触发器的SQL语法
DELIMITER|
CREATETRIGGER``.``
<[BEFORE|AFTER]><[INSERT|UPDATE|DELETE]>
ON
FOREACHROW
BEGIN
--dosomething
END|
1.3触发器的优点
触发器可通过数据库中的相关表实现级联更改;不过,通过级联引用完整性约束可以更有效地执行这些更改。
触发器可以强制比用CHECK约束定义的约束更为复杂的约束。
与CHECK约束不同,触发器可以引用其它表中的列。
例如,触发器可以使用另一个表中的SELECT比较插入或更新的数据,以及执行其它操作,如修改数据或显示用户定义错误信息。
触发器也可以评估数据修改前后的表状态,并根据其差异采取对策。
一个表中的多个同类触发器(INSERT、UPDATE或DELETE)允许采取多个不同的对策以响应同一个修改语句。
1.4比较触发器与约束
约束和触发器在特殊情况下各有优势。
触发器的主要好处在于它们可以包含使用Transact-SQL代码的复杂处理逻辑。
因此,触发器可以支持约束的所有功能;但它在所给出的功能上并不总是最好的方法。
实体完整性总应在最低级别上通过索引进行强制,这些索引或是PRIMARYKEY和UNIQUE约束的一部分,或是在约束之外独立创建的。
假设功能可以满足应用程序的功能需求,域完整性应通过CHECK约束进行强制,而引用完整性(RI)则应通过FOREIGNKEY约束进行强制。
在约束所支持的功能无法满足应用程序的功能要求时,触发器就极为有用。
例如:
除非REFERENCES子句定义了级联引用操作,否则FOREIGNKEY约束只能以与另一列中的值完全匹配的值来验证列值。
CHECK约束只能根据逻辑表达式或同一表中的另一列来验证列值。
如果应用程序要求根据另一个表中的列验证列值,则必须使用触发器。
约束只能通过标准的系统错误信息传递错误信息。
如果应用程序要求使用(或能从中获益)自定义信息和较为复杂的错误处理,则必须使用触发器。
触发器可通过数据库中的相关表实现级联更改;不过,通过级联引用完整性约束可以更有效地执行这些更改。
触发器可以禁止或回滚违反引用完整性的更改,从而取消所尝试的数据修改。
当更改外键且新值与主键不匹配时,此类触发器就可能发生作用。
例如,可以在titleauthor.title_id上创建一个插入触发器,使它在新值与titles.title_id中的某个值不匹配时回滚一个插入。
不过,通常使用FOREIGNKEY来达到这个目的。
如果触发器表上存在约束,则在INSTEADOF触发器执行后但在AFTER触发器执行前检查这些约束。
如果约束破坏,则回滚INSTEADOF触发器操作并且不执行AFTER触发器。
触发器到底可不可以在视图上创建在SQLServer™联机丛书中,是没有说触发器不能在视图上创建的,并且在语法解释中表明:
在CREATETRIGGER的ON之后可以是视图。
然而,事实似乎并不是如此,很多专家也说触发器不能在视图上创建。
我也专门作了测试,的确如此,不管是普通视图还是索引视图,都无法在上面创建触发器,真的是这样吗?
请点击详细,但是无可厚非的是:
当在临时表或系统表上创建触发器时会遭到拒绝。
深刻理解FORCREATETRIGGER语句的FOR关键字之后可以跟INSERT、UPDATE、DELETE中的一个或多个,也就是说在其它情况下是不会触发触发器的,包括SELECT、TRUNCATE、WRITETEXT、UPDATETEXT。
相关内容一个有趣的应用我们看到许多注册系统在注册后都不能更改用户名,但这多半是由应用程序决定的,如果直接打开数据库表进行更改,同样可以更改其用户名,在触发器中利用回滚就可以巧妙地实现无法更改用户名……详细内容触发器内部语句出错时……这种情况下,前面对数据更改操作将会无效。
举个例子,在表中插入数据时触发触发器,而触发器内部此时发生了运行时错误,那么将返回一个错误值,并且拒绝刚才的数据插入。
不能在触发器中使用的语句触发器中可以使用大多数T-SQL语句,但如下一些语句是不能在触发器中使用的。
CREATE语句,如:
CREATEDATABASE、CREATETABLE、CREATEINDEX等。
ALTER语句,如:
ALTERDATABASE、ALTERTABLE、ALTERINDEX等。
DROP语句,如:
DROPDATABASE、DROPTABLE、DROPINDEX等。
DISK语句,如:
DISKINIT、DISKRESIZE。
LOAD语句,如:
LOADDATABASE、LOADLOG。
RESTORE语句,如:
RESTOREDATABASE、RESTORELOG。
RECONFIGURE
TRUNCATETABLE语句在sybase的触发器中不可使用!
(本人:
说的似乎不正确)
1.5慎用触发器
触发器功能强大,轻松可靠地实现许多复杂的功能,为什么又要慎用呢。
触发器本身没有过错,但由于我们的滥用会造成数据库及应用程序的维护困难。
在数据库操作中,我们可以通过关系、触发器、存储过程、应用程序等来实现数据操作……同时规则、约束、缺省值也是保证数据完整性的重要保障。
如果我们对触发器过分的依赖,势必影响数据库的结构,同时增加了维护的复杂程序。
1.6触发器命名规则
命名规则:
对象名称_状态_DML命令_类型
对象名称:
指表名或者视图名称;
状态:
after(AFT)、before(BEF)、instead(INS);
DML命令:
insert(INS)、update(UPD)、delete(DEL),
两个DML命令的写法(INS_UPD),
三具DML命令的写法IUD;
类型:
语句、行(ROW)。
2触发器应用
参考资料:
《
2.1功能
1、允许/限制对表的修改
2、自动生成派生列,比如自增字段
3、强制数据一致性
4、提供审计和日志记录
5、防止无效的事务处理
6、启用复杂的业务逻辑
2.2触发器的组成部分
1、触发器名称
2、触发语句
3、触发器限制
4、触发操作
2.2.1触发器名称
createtriggerbiufer_employees_department_id
命名习惯:
biufer(beforeinsertupdateforeachrow)
employees表名
department_id列名
2.2.2触发语句
比如:
表或视图上的DML语句
DDL语句
数据库关闭或启动,startupshutdown等等
beforeinsertorupdate
ofdepartment_id
onemployees
referencingoldasold_value
newasnew_value
foreachrow
说明:
1、无论是否规定了department_id,对employees表进行insert的时候
2、对employees表的department_id列进行update的时候
3、触发器限制
when(new_value.department_id<>80),限制不是必须的。
此例表示如果列department_id不等于80的时候,触发器就会执行。
其中的new_value是代表跟新之后的值。
4、触发操作
是触发器的主体
begin
:
new_mission_pct:
=0;
end;
主体很简单,就是将更新后的commission_pct列置为0
触发:
insertintoemployees(employee_id,
last_name,first_name,hire_date,job_id,email,department_id,salary,commission_pct)
values(12345,’Chen’,’Donny’,sysdate,12,‘donny@’,60,10000,.25);
selectcommission_pctfromemployeeswhereemployee_id=12345;
触发器不会通知用户,便改变了用户的输入值。
2.3触发器类型
1、语句触发器
2、行触发器
3、INSTEADOF触发器
4、系统条件触发器
5、用户事件触发器
2.3.1语句触发器
是在表上或者某些情况下的视图上执行的特定语句或者语句组上的触发器。
能够与INSERT、UPDATE、DELETE或者组合上进行关联。
但是无论使用什么样的组合,各个语句触发器都只会针对指定语句激活一次。
比如,无论update多少行,也只会调用一次update语句触发器。
2.3.1.1安全检查
需要对在表上进行DML操作的用户进行安全检查,看是否具有合适的特权。
1、创建数据表
Createtablefoo(anumber);
2、创建触发器
CreateOrReplaceTriggerbiud_foo
Beforeinsertorupdateordelete
Onfoo
declare
not_library_userexception;
Begin
Ifusernotin('WHN')then
RAISEnot_library_user;
Endif;
EXCEPTION
WHENnot_library_userTHEN
RAISE_APPLICATION_ERROR(-20001,
'Youdonthaveaccesstomodifythistable.');
End;
/
3、测试
即使SYS,SYSTEM用户也不能修改foo表中的数据。
insertintowhn.foovalues(11,sysdate);
2.3.1.2记录dml操作日志
对修改表的时间、人物进行日志记录。
1、建立试验表
createtableemployees_copyasselect*fromhr.employees
2、建立日志表
--Createtable
createtableEMPLOYEES_LOG
(
WHOVARCHAR2(30),
WHENDATE,
ACTIONCHAR
(1)
);
3、创建触发器
在employees_copy表上建立语句触发器,在触发器中填充employees_log表。
CreateOrReplaceTriggerbiud_employee_copy
BeforeinsertorupdateordeleteOnemployees_copy
Declare
L_actionemployees_log.action%type;
Begin
ifinsertingthen
L_action:
='I';
elsifupdatingthen
L_action:
='U';
elsifdeletingthen
L_action:
='D';
else
raise_application_error(-20001,
'Youshouldneverevergetthiserror.');
endif;
Insertintoemployees_log
(Who,when,action)
Values
(user,sysdate,L_action);
End;
/
4、测试
updateemployees_copysetsalary=salary*1.1;
5、查询
select*fromemployess_log;
2.3.2行触发器
是指为受到影响的各个行激活的触发器,定义与语句触发器类似,有以下两个例外:
1、定义语句中包含FOREACHROW子句
2、在BEFORE……FOREACHROW触发器中,用户可以引用受到影响的行值。
比如:
定义:
createtriggerbiufer_employees_department_id
beforeinsertorupdate
ofdepartment_id
onemployees_copy
referencingoldasold_value
newasnew_value
foreachrow
when(new_value.department_id<>80)
begin
:
new_mission_pct:
=0;
end;
/
Referencing子句:
执行DML语句之前的值的默认名称是:
old,之后的值是:
new
insert操作只有:
new
delete操作只有:
old
update操作两者都有
referencing子句只是将new和old重命名为new_value和old_value,目的是避免混淆。
比如操作一个名为
new的表时。
作用不很大。
2.3.2.1为主健生成自增序列号
1、创建数据表
createtablefoo(idnumber,datavarchar2(20));
2、创建序列
createsequencefoo_seq;
3、创建触发器
createorreplacetriggerbifer_foo_id_pk
beforeinsertonfoo
foreachrow
begin
selectfoo_seq.nextvalinto:
new.idfromdual;
end;
/
4、插入数据测试
insertintofoo(data)values(‘donny’);
insertintofoovalues(5,’Chen’);
5、查询
select*fromfoo;
2.3.2.2备份数据(插入、修改、删除)
1、创建测试表
createtableemployees_copyasselect*fromhr.employees;(上面已经创建)
2、创建备份表
createtableEmployees_bakasselect*fromemployees_copywhere1=0;
3、创建触发器
createorreplacetriggeremployees_copy_bef_dui
beforeinsertorupdateordeleteonemployees_copy
referencingoldasold_valuenewasnew_value
foreachrow
--when(new_value.department_id<>80)
begin
ifinsertingthen
insertintoemployees_bak
values
(:
new_value.EMPLOYEE_ID,
:
new_value.FIRST_NAME,
:
new_value.LAST_NAME,
:
new_value.EMAIL,
:
new_value.PHONE_NUMBER,
:
new_value.HIRE_DATE,
:
new_value.JOB_ID,
:
new_value.SALARY,
:
new_value.COMMISSION_PCT,
:
new_value.MANAGER_ID,
:
new_value.DEPARTMENT_ID);
else
insertintoemployees_bak
values
(:
old_value.EMPLOYEE_ID,
:
old_value.FIRST_NAME,
:
old_value.LAST_NAME,
:
old_value.EMAIL,
:
old_value.PHONE_NUMBER,
:
old_value.HIRE_DATE,
:
old_value.JOB_ID,
:
old_value.SALARY,
:
old_value.COMMISSION_PCT,
:
old_value.MANAGER_ID,
:
old_value.DEPARTMENT_ID);
endif;
end;
/
4、测试
插入、删除、修改employees_copy表中的数据。
5、查看
select*fromemployees_bakt;
2.3.2.3对数据操作的约束
根据用户和数据表中某一个字段的值来限定修改和删除,只有指定的用户才可以修改和删除。
1、创建数据表
createtableTRG_EMPLOYESSasselect*fromHR.EMPLOYEES;
2、创建触发器
当TRG_EMPLOYESS中HIRE_DATE大于1999-1-1或者SALARY大于10000时,数据不能被其它用户修改或删除,只有USER_NAME这个用户可以删除。
createorreplacetriggerTRG_EMPLOYESS_BEF_DU
beforeupdateordeleteonTRG_EMPLOYESS
referencingoldasold_valuenewasnew_value
foreachrow
when(old_value.HIRE_DATE>to_date('1990-1-1','yyyy-mm-dd')orold_value.SALARY>10000)
begin
ifupdatingand(:
old_value.SALARY>10000or:
old_value.HIRE_DATE>to_date('1999-1-1','yyyy-mm-dd'))anduser<>'WHN'then
RAISE_APPLICATION_ERROR(-20002,'你无权修改此数据,请联系管理员!
');
endif;
ifdeletingand(:
old_value.SALARY>10000or:
old_value.HIRE_DATE>to_date('1999-1-1','yyyy-mm-dd'))anduser<>'WHN'then
RAISE_APPLICATION_ERROR(-20001,'你无权删除此数据,请联系管理员!
');
endif;
end;
3、测试
deletefromwhn.trg_employesstwheret.employee_id=199;
updatewhn.trg_employesstsett.last_name=t.last_name||'11'wheret.employee_id=199;
deletefromwhn.trg_employesstwheret.employee_id=205;
updatewhn.trg_employesstsett.last_name=t.last_name||'11'wheret.employee_id=205;
2.3.3INSTEADOF触发器更新视图
1、创建视图
Createorreplaceviewvw_trg_employessas
Selectfirst_name||','||last_namename,
email,
phone_number,
employee_idemp_id
Fromtrg_employess;
2、尝试更新email和name
--name无法修改,提示“ORA-01733:
此处不请允许虚拟列”
updatevw_trg_employess
setname='Chen1,Donny1',email='163'
whereemp_id=202;
--email可以修改
updatevw_trg_employess
setemail='163'
whereemp_id=202
3、创建触发器
createorreplacetriggerVW_TRG_EMPLOYESS_INS_UPD
INSTEADOFUpdateonVW_TRG_EMPLOYESS
Begin
UpdateTRG_EMPLOYESS
SetEMPLOYEE_ID=:
new.EMP_ID,
FIRST_NAME=substr(:
new.NAME,instr(:
new.NAM