本次实验的目的是为了使读者加深对数据完整性的理解,学会常见和使用触发器。
触发器原理解析:
1.触发器概述
触发器是SQL语言提供的一种维护数据完整性的工具。触发器过程时由程序员给定,如一个和完整性控制动作有关的处理过程。当系统规定的出发条件发生时,给定的过程被调用。触发条件是多种多样的,例如:进入或退出程序的某层结构(如BLOCK,FORM 等);查询、修改等操作发生之前或之后‘某个按键动作;TRIGGER过程调用(相当于子程序调用)。
触发器是实施复杂完整性的特殊类型的存储类型。触发器不需要专门语句调用,对所保护数据进行修改时自动激活,以防止对数据进行不正确,未授权或不一致的修改。
2.触发器的类型及其具有的特殊表
一个触发器值适用于一个表,每个表最多只能有三个触发器,分别是INSERT,UPDATE,DELETE触发器。触发器仅在实施数据完整性和处理业务规则时使用。
每个触发器有两个特殊的表,即插入表(INSERTED TABEL)和删除表(DELETED TABLE)。这两个表示逻辑表,并且这两个表示由系统管理的,存储在内存中,不是存储在数据库中。因此不允许直接对其修改,并且这两个表的结构总是与被触发器作用的表有相同的表结构。
3.三种触发器的工作原理
INSERT触发器:发向INSERTED表中插入一个新行的副本。然后检查INSERTED表中的新行是否有效 ,确定是否要组织该插入操作。如果所插入的行中的值是有效的,则将该行插入到触发器表。
UPDATE触发器:先将原始数据行移到DELETED表中。然后将一个新行插入INSERTED表中。最后计算DELETED表和INSERTED表中的值以确定是否进行干预。
DELETE触发器:将原始数据行移到DELETED表中,计算DELETED表中的值决定是否进行干预,如果不进行,那么把数据行删除。
4.SQL中创建触发器的语法
GO
CREATE TRIGGER <触发器> ON <表明 | 视图名>
[ WITH ENCRYPTIOM ]
{ FOR | AFTER | INSTEAD OF } { [ DELETE ] [ , ] [ INSERT ] [ , ] [ UPDATE ] }
[ WITH | APPEND ]
[ NOT FOR REPLICATION ]
AS <SQL语句组>
注意:创建触发器的语句一定要是SQL批处理的第一局。
5.INSEAD OF 触发器
在创建触发器时指定INSTEAD OF 选项,表示数据库将不执行触发SQL语句二替换惩治性相应的触发器操作。在表或视图上,每个INSERT,UPDATE或DELETE语句最多可以定义一个INSTEAD OF 触发器。然而,可以在每个具有INSTEAD OF 触发器的视图上定义视图。INSTEAD OF 触发器主要用于使用不能更新的视图支持更新,并且允许选择性地拒绝批处理中某些处分的操作。
INSTEAD OF 触发器不能定义在WITH CHECK OPTION的可更新视图上。同时,在含有使用DELETE或UPDATE级联操作定义的外键的表上也不能定义INSTEAD OF DELETE 和INSTEAD OF UPDATE触发器。
6.触发器和存储过程的区别
(1)是否附属于唯一的表。触发器附属于唯一的表,而存储过程不附属于任何的表。
(2)是否事件驱动。触发器由事件驱动,而存储过程由显式的指令调用。
(3)是否有数量的限制,一般不允许建立太多的触发器,对触发器的数目有要求,而存储过程没有要求。
以下是本次实验的练习与习题答案:(school表在此并未发出,请参考之前的练习)
USE SCHOOL --2.3.4实验练习 --(1)创建WORKER表,并自定义两个约束U1以及U2,其中U1规定NAME字段唯一,U2规定SAGE(级别)字段的上限是28。 CREATE TABLE WORKER ( NUMBER CHAR(5), NAME CHAR(8) CONSTRAINT U1 UNIQUE, SEX CHAR(1), SAGE INT CONSTRAINT U2 CHECK(SAGE<=28), DEAPARTMENT CHAR(20), CONSTRAINT PK_WORKER PRIMARY KEY(NUMBER) ) SELECT * FROM WORKER --(2)在WORKER表中插入一条合法记录。 INSERT INTO WORKER(NUMBER,NAME,SEX,SAGE,DEAPARTMENT) VALUES('00001','张三','M',20,'CS'); SELECT * FROM WORKER; --(3)演示插入违反U2约束的例子,U2规定元组的SAGE属性的值必须小于等于28. INSERT INTO WORKER(NUMBER,NAME,SEX,SAGE,DEAPARTMENT) VALUES('00002','李四','M',29,'CS'); INSERT INTO WORKER(NUMBER,NAME,SEX,SAGE,DEAPARTMENT) VALUES('00002','李四','M',28,'CS'); SELECT * FROM WORKER; --(4)去除U2约束。 ALTER TABLE WORKER DROP U2; --(5)重新插入(3)中想要插入的数据,由于去除了U2约束,所以插入成功。 INSERT INTO WORKER(NUMBER,NAME,SEX,SAGE,DEAPARTMENT) VALUES('00003','王四','M',29,'IS'); SELECT * FROM WORKER; --(6)创建规则RULE_SEX,规定插入或更新的值只能是M或F,并绑定到WORKER的SEX字段。 GO CREATE RULE RULE_SEX AS @VALUE IN ('F','M'); GO EXEC SP_BINDRULE RULE_SEX, 'WORKER.[SEX]'; --(7)演示违反规则RULE_SEX的插入操作。 INSERT INTO WORKER VALUES('00004','王浩','1','25','CS'); INSERT INTO WORKER VALUES('00004','王浩','F','25','CS'); SELECT * FROM WORKER; --2.3.4自我实践 --(1)加入约束U3,令SAGE的值大于等于0. ALTER TABLE WORKER ADD CONSTRAINT U3 CHECK(SAGE>=0); --(2)加入规则R2,确保插入的记录的SAGE值在1到100之间,并绑定到SAGE属性上。 GO CREATE RULE R2 AS @VALUE BETWEEN 1 AND 100 GO EXEC SP_BINDRULE R2, 'WORKER.[SAGE]'; USE SCHOOL --2.4.4实验练习 --(1)为WORKER表建立触发器T1,当插入或是更新表中数据时,保证所操作的记录的SAGE值大于0. --****首先依旧使用前一博客中所用到的WORKER表 GO CREATE TRIGGER T1 ON WORKER FOR INSERT,UPDATE AS IF(SELECT SAGE FROM INSERTED)<1 BEGIN PRINT 'SAGE MUST BE A INTEGER MORE THAN ZERO! TRANSACION FAIL' ROLLBACK TRANSACTION END --(2)为WORKER表建立触发器T2,禁止删除编号为00001的CEO。 GO CREATE TRIGGER T2 ON WORKER FOR DELETE AS IF(SELECT NUMBER FROM DELETED)='00001' BEGIN PRINT 'HE IS THE CEO!DELETE FAIL!' ROLLBACK TRANSACTION END --(3)WORKER表中的人员的编号是不可改变的,创建触发器T3实现更新中编号的不可改变性。 GO CREATE TRIGGER T3 ON WORKER FOR UPDATE AS IF UPDATE(NUMBER) BEGIN PRINT 'EVERY NUMBER CANNOT BE CHANGED!' ROLLBACK TRANSACTION END --(4)演示违反T1触发器的约束的插入操作。 SELECT * FROM WORKER; INSERT INTO WORKER VALUES('00005','李红','F','-10','CS'); --(5)演示违反T1触发器的约束的更新操作。 SELECT * FROM WORKER; UPDATE WORKER SET SAGE= -7 WHERE NUMBER='00001'; --(6)演示违反T2触发器的约束的删除操作。 SELECT * FROM WORKER; DELETE FROM WORKER WHERE NAME='张三'; --(7)演示违反T2触发器的约束的更新操作。 SELECT * FROM WORKER; UPDATE WORKER SET NUMBER='00007' WHERE SEX='F'; --(8)演示INSTEAD OF 触发器在不可更新视图上的运用。 CREATE VIEW STUDENTSCHOLARSHIP AS SELECT ST.SID,ST.SNAME,ST.GRADE,SC.R_MONEY FROM STUDENTS ST,SCHOLARSHIP SC WHERE ST.SID=SC.STU_ID INSERT INTO STUDENTSCHOLARSHIP VALUES('1000','JOHN','2003',1500) --*****注:此时会出现错误,“视图或函数'studentscholarship'不可更新,因为修改会影响多个基表。”针对该问题创建一个INSTEAD OF 触发器即可 CREATE TRIGGER TRI_INS_STU_SCHOLARSHIP ON STUDENTSCHOLARSHIP INSTEAD OF INSERT AS BEGIN SET NOCOUNT ON IF(NOT EXISTS (SELECT S.SID FROM STUDENTS S,INSERTED I WHERE S.SID=I.SID ) ) BEGIN INSERT INTO STUDENTS SELECT SID,SNAME,NULL,GRADE FROM INSERTED DECLARE @MAX_M_ID VARCHAR(10) SELECT @MAX_M_ID = MAX(M_ID) FROM SCHOLARSHIP INSERT INTO SCHOLARSHIP SELECT @MAX_M_ID + 1,SID,R_MONEY FROM INSERTED END ELSE PRINT '数据已经存在!' END --*****注:该触发器是将原本一次性插入到STUDENTSCHOLARSHIP视图的INSERT语句进行分解,从而避免了一次对多个基表进行操作。创建触发器后再次尝试更新视图 INSERT INTO STUDENTSCHOLARSHIP VALUES('1000','JOHN','2003',1500) SELECT * FROM STUDENTSCHOLARSHIP SELECT * FROM STUDENTS WHERE SID = '1000' SELECT * FROM SCHOLARSHIP WHERE STU_ID='1000'
针对以上练习,一下是自我练习题并附以答案:
--2.4.5自我实践 --(1)建立一个在WORKER表上的触发器T4,要求插入记录的SAGE值必须必表中已记录的最大SAGE值大。 GO CREATE TRIGGER T4 ON WORKER FOR INSERT AS IF(SELECT SAGE FROM INSERTED)<=(SELECT MAX(SAGE) FROM WORKER) BEGIN PRINT 'THE SAGE OF COUPLE MUST BE MORE THAN THE EXISTED COUPLES SAGE!' ROLLBACK TRANSACTION END --(2)建立一个在WORKER表上的触发器T5,要求当更新一个记录的时候,表中记录的SAGE值要比老记录的SAGE值大,因为一般工资级别只能升不能降。 GO CREATE TRIGGER T5 ON WORKER FOR UPDATE AS IF(SELECT SAGE FROM INSERTED)<=(SELECT SAGE FROM DELETED) BEGIN PRINT 'THE SAGE OF NEW COUPLE MUST BE MORE THAN THE SAGE OF OLD COUPLE!' ROLLBACK TRANSACTION END