您的位置:首页 > 数据库

SQL SERVER 行级触发器(准)

2008-01-31 16:12 309 查看

SQL SERVER  准行级触发器

ⅰDeleted表和Inserted表

   SQL SERVER只有表级和服务器级(相当)的触发器,在很多需要使用行级触发器的场合,很多同事都开始怀念使用Oracle的日子。但是事实上,我认为SQL SERVER这样做有它的理由(或难处),甚至于很多时候我会说,我们不需要精细到以行来触发的操作,因为我们往往要做的是处理哪些数据而不是哪条数据。不过如果您真的很需要,那么SQL SERVER提供了相应的手段足以让您实现类似的功能,并且应有更大的灵活性。

   对于SQL SERVER,一个添修删操作对应于确定的触发器只会作用一次,而不管在这个操作中有多少纪录受到了该次操作的影响,因为它是语句级的。但这并不意味着我们需要去遍历比较整个表才可以得知哪些记录发生了改变。在DML触发器开发中,我们可以通过Deleted表Inserted表得到激活当前触发器的操作中那些受到影响的记录。

   对于这两个表,我们可以这样理解。在一个添修删操作过程中,SQL引擎生成了这两个临时表。他把删除掉的数据暂时存储在Deleted表内,把要追加的数据存储在Inserted表内。而对于修改来说,我们把他理解为删除旧记录和加入新记录两个步骤,那么就是说他在两个表内分别存储了目标记录修改前后的数据。

   想要检验这种猜测的合理性,我们不妨用下面的例子试试看。


USE [myTestDataBase]




-- 初始化


IF OBJECT_ID ('tbStudents', 'TABLE') IS NOT NULL


   DROP TABLE [tbStudents]


GO  


IF OBJECT_ID ('tbStudentsLog', 'TABLE') IS NOT NULL


   DROP TABLE [tbStudentsLog]


GO 


IF OBJECT_ID ('TR_StudentNameChanged', 'TR') IS NOT NULL


   DROP TRIGGER TR_StudentNameChanged


GO




-- 学生表(包含学号和姓名两个字段)


CREATE TABLE [dbo].[tbStudents](


    [StudentID]   [nchar](10) COLLATE Japanese_CI_AS NOT NULL,


    [StudentName] [nchar](10) COLLATE Japanese_CI_AS NULL,


 CONSTRAINT [PK_tbStudents] PRIMARY KEY CLUSTERED ( [StudentID] ASC )





GO




-- 学生记录表(包含学号、现在姓名、曾用名三个字段)


CREATE TABLE [dbo].[tbStudentsLog](


    [StudentID]   [nchar](10) COLLATE Japanese_CI_AS NOT NULL,


    [StudentNewName] [nchar](10) COLLATE Japanese_CI_AS NULL,


    [StudentOldName] [nchar](10) COLLATE Japanese_CI_AS NULL,


 CONSTRAINT [PK_tbStudentsLog] PRIMARY KEY CLUSTERED ( [StudentID] ASC )





GO




--定义[tbStudents]表被修改时的触发器


CREATE TRIGGER TR_StudentNameChanged


ON [tbStudents]


AFTER UPDATE


AS


     --将被修改学生记录的修改前后的值存入学生记录


     INSERT [tbStudentsLog]                                                        


     SELECT [Inserted].[StudentID]


          , [Inserted].[StudentName] AS [StudentNewName]


          , [Deleted].[StudentName] AS [StudentOldName]


       FROM [Inserted] 


      INNER JOIN [Deleted] ON [Deleted].[StudentID] = [Inserted].[StudentID]                                            


      WHERE NOT EXISTS(


             SELECT * 


               FROM [tbStudentsLog] 


              WHERE [tbStudentsLog].[StudentID] = [Inserted].[StudentID]) 


GO




--加入两个学生            


INSERT [dbo].[tbStudents] VALUES ('0000000001','小臭')


INSERT [dbo].[tbStudents] VALUES ('0000000002','小美')




--检查学生和学生纪录


SELECT * FROM [dbo].[tbStudents]


SELECT * FROM [dbo].[tbStudentsLog]




--修改一个学生的姓名


UPDATE [dbo].[tbStudents] SET [StudentName] = '大红花' WHERE [StudentID] = '0000000001'




--验证触发器地执行结果


SELECT * FROM [dbo].[tbStudents]


SELECT * FROM [dbo].[tbStudentsLog]

执行的结果:



我们把修改学生的姓名的SQL文的WHERE子句删除掉,替换成下面的语句再试试看


--修改所有学生的姓名


UPDATE [dbo].[tbStudents] SET [StudentName] = '全改掉' 

执行后的结果是



 

ⅱ实现行级触发器

        为了实现实现行级触发器,我们需要动用SQL游标,但是在触发器内一般的做法是不建议使用游标。因此这里只记录下这个思路,如果您真的要实现它,那么我想不是多么复杂。但是我想,一个程序员一旦应有了这种自主性,那么它往往就不会真的就只在这个触发器内模拟一个行级触发的功能吧。或许有一天我会真的需要它,那时候再来补上这段代码吧。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息