数据库复习(十)——触发器

10.1  触发器的创建与管理

       触发器是一种特殊的存储过程,常常用于实现强制业务规则和数据完整性。触发器由SQL Server自动执行,不能由应用程序调用,这是它与存储过程不同的地方,便于保护数据库的完整性和完全性。

触发器在对表进行操作时(UPDATE、INSERT 或 DELETE)激活。

1.使用触发器有如下优点

       触发器是自动的:它们在对表的数据作了任何修改(比如手工输入或者应用程序采取的操作)之后立即被激活。触发器可以通过数据库中的相关表进行层叠更改。这比直接把代码写在前台的做法更安全合理。

      触发器可以强制限制,这些限制比用 CHECK 约束所定义的更复杂。与 CHECK 约束不同的是,触发器可以引用其它表中的列。

2.触发器用CREATE TRIGGER语句创建。语法如下:

CREATE TRIGGER trigger_name

ON { table | view }

[ WITH ENCRYPTION ]

{

    { { FOR | AFTER | INSTEAD OF } { [ INSERT ] [ , ] [ UPDATE ] }

        [ WITH APPEND ]

        [ NOT FOR REPLICATION ]

        AS

        [ { IF UPDATE ( column )

            [ { AND | OR } UPDATE ( column ) ]

                [ ...n ]

        | IF ( COLUMNS_UPDATED ( ) { bitwise_operator } updated_bitmask )

                { comparison_operator } column_bitmask [ ...n ]

        } ]

        sql_statement [ ...n ]

    }

}

    Microsoft 不支持在系统表上添加用户定义触发器。

3.例题使用的表格

CREATE TABLE Company(

        CompanyID  varchar (10) NOT NULL ,

              CompanyName varchar (30) NOT NULL

                    )    

CREATE TABLE Contract (

        ContractID varchar (10) NOT NULL ,

        CompanyID varchar (10) NOT NULL ,

        ContractName varchar (30) NULL ,

        ContractVolume numeric(18, 2) NULL,

        SignDate Datetime NULL)

 

    CREATE TABLE ContractDetail (

        ContractID varchar (10) NOT NULL ,

        ContractDetailID varchar (10) NOT NULL ,

        Volume numeric(18, 2) NULL)

 

例1:创建一个触发器,当公司信息表发生变化时,则发送一封邮件到用户”RED”,发送邮件用用系统存储过程xp_sendmail。

CREATE TRIGGER attetion

ON Company

FOR INSERT, UPDATE, DELETE

AS

EXEC master..xp_sendmail 'RED', ‘公司信息表已发生变化!’

4.修改和删除触发器

修改触发器用ALTER TRIGGER,语法如下:

ALTER TRIGGER trigger_name

ON ( table | view )

[ WITH ENCRYPTION ]

{

    { ( FOR | AFTER | INSTEAD OF ) { [ DELETE ] [ , ] [ INSERT ] [ , ] [ UPDATE ] }

        [ NOT FOR REPLICATION ]

        AS

        sql_statement [ ...n ]

    }

    |

    { ( FOR | AFTER | INSTEAD OF ) { [ INSERT ] [ , ] [ UPDATE ] }

        [ NOT FOR REPLICATION ]

        AS

        { IF UPDATE ( column )

        [ { AND | OR } UPDATE ( column ) ]

        [ ...n ]

        | IF ( COLUMNS_UPDATED ( ) { bitwise_operator } updated_bitmask )

        { comparison_operator } column_bitmask [ ...n ]

        }

        sql_statement [ ...n ]

    }

}

 

删除触发器用DROP TRIGGER,可以同时删除一个或多个触发器,语法如下:

DROP TRIGGER { trigger } [ ,...n ]

 

例2:先创建一个触发器,当有用户向公司信息表Company中添加记录时,向客户端打印一条用户定义消息,然后,使用 ALTER TRIGGER 语句使该触发器不仅对 INSERT 活动有效,而且当有用户删除或修改公司信息表中的数据时,触发器仍有效。

创建触发器

CREATE TRIGGER Company_attention

ON Company

WITH ENCRYPTION

FOR INSERT

AS RAISERROR (50001, 16, 10)

修改触发器

ALTER TRIGGER Company_attention

ON Company

WITH ENCRYPTION

FOR INSERT,DELETE,UPDATE

AS RAISERROR (50001, 16, 10)

例3:删除触发器单位信息表上的触发器Company_attention。

DROP TRIGGER Company_attention

 

5.用触发器实现强制业务规则

       用触发器可以实现强制的业务规则,常用的方法有:用INSERT、UPDATE、DELETE、INSTEAD OF触发器来实现、用嵌套触发器来实现、用递规触发器来实现。

INSERT 触发器     

INSERT触发器当向表中添加记录时触发,为了维护数据完整性,当表中添加了新的记录后,应该对其关联表的数据进行调整,以实时反应数据的变化。如进销存系统中的库存单价管理,当有新的产品进入库存后,应当改变库存单价。

需要用到inserted表和deleted表,这是两个逻辑表,由系统来维护,不允许用户直接对这两个表进行修改,它们存放于内存中,不存放在数据库中,它们的表结构与被触发器作用的表结构相同,触发器工作完成,这两个表也被删除。

 

   

Inserted

   存放由insert 或update语句的执行而导致要加到该触发器作用的表中去的任何新行。

deleted

  存放由delete 或update语句的执行而导致要从被该触发器作用的表中删除的任何行。

 

例3:在一个进销存系统中,库存表记录所有产品的现库存情况,包括产品号、库存数量、单价STORAGE(ProductID,Amount,Price),

    入库表单包括产品号、入库数量、入库单价、入库金额、入库日期,Entry (ProductID, Amount, Price, Volume, EntryDate,)。

CREATE TABLE Storage

    (ProductID varchar(10) NOT NULL,

    Amount int NOT NULL,   

    Price money NOT NULL)

CREATE TABLE Entry

    (ProductID varchar(10) NOT NULL,

    Amount int NOT NULL,

    Price money NOT NULL,

    Volume money NOT NULL,

    EntryDate Datetime NOT NULL)

 

当有新的产品入库时,需要及时更新库存表中的库存数量和库存单价,因此需要在入库表Entry上建立INSERT触发器。

CREATE TRIGGER Storage_price

ON Entry

FOR INSERT

AS

UPDATE Storage SET

Storage.Amount=Storage.Amount+Inserted.Amount,

Storage.Price=

(Storage.Price*Storage.Amount+Inserted.Volume)/( Storage.Amount+Inserted.Amount)

FROM Storage,Inserted

WHERE Storage.ProductID=Inserted.ProductID

 

UPDATE 触发器     

UPDATE触发器当更新表中记录时触发,可以用COLUMNS_UPDATED()来测试是否更新了指定的列。COLUMNS_UPDATED 返回 varbinary 位模式,表示插入或更新了表中的哪些列。COLUMNS_UPDATED 函数以从左到右的顺序返回位,最左边的位表示表中的第一列;向右的下一位表示第二列,依此类推

 

例4:

合同表中有公司代码字段,当公司信息表中公司代码变化时,应更改合同表中相应的记录,使合同表中的公司代码能及时指向新的代码。应在公司信息表中建立UPDATE触发器,以实现连锁更新。

CREATE TRIGGER [COMPANY_UPDATE] ON Company

FOR UPDATE

AS

BEGIN

   DECLARE @i_COMPANYID varchar(20),@d_COMPANYID varchar(20)

   DECLARE COMPANY_insertcursor cursor for –插入游标

                   SELECT COMPANYID FROM INSERTED

   DECLARE COMPANY_deletecursor cursor for –删除游标

                SELECT COMPANYID FROM DELETED

   OPEN COMPANY_insertCursor  --打开游标

   OPEN COMPANY_deletecursor

 

FETCH NEXT FROM COMPANY_insertCursor INTO @i_COMPANYID

   FETCH NEXT FROM COMPANY_deleteCursor INTO @d_COMPANYID

   WHILE @@FETCH_STATUS = 0

   BEGIN

   IF @i_COMPANYID<>@d_COMPANYID

   BEGIN

         UPDATE CONTRACT SET

COMPANYID=@i_COMPANYID WHERE COMPANYID=@d_COMPANYID

     

   END

      FETCH NEXT FROM COMPANY_insertCursor INTO @i_COMPANYID

      FETCH NEXT FROM COMPANY_deleteCursor INTO @d_COMPANYID

   END

   CLOSE COMPANY_insertCursor      --关闭游标

   DEALLOCATE COMPANY_insertCursor –释放游标

   CLOSE COMPANY_deleteCursor

   DEALLOCATE COMPANY_deleteCursor

END

 

DELETE 触发器

DELETE触发器当删除表中数据时触发,用它可以实级联删除。

例5:在一个进销存系统中,库存表记录所有产品的现库存情况,包括产品号、库存数量、单价,STORAGE(ProductID,Amount,Price)入库表单包括产品号、入库数量、入库单价、入库金额、入库日期,Entry (ProductID, Amount, Price, Volume, EntryDate,)。当入库单中删除一个记录时,需要及时更新库存表中的库存数量和库存单价,因此需要在入库表Entry上建立DELETE触发器。

   

CREATE TRIGGER Storage_price_delete

    ON Entry

    FOR DELETE

    AS

    UPDATE Storage SET

    Storage.Amount=Storage.Amount-Deleted.Amount,

    Storage.Price=

    (Storage.Price*Storage.Amount-Deleted.Volume)/( Storage.Amount-   Deleted.Amount)

    FROM Storage,Deleted

    WHERE Storage.ProductID=Deleted.ProductID

 

例6:合同明细表是合同表的从表,当删除合同表中的一个记录时,应该从合同明细表中把与该记录相关的记录删除,实现级联删除。

CREATE TRIGGER [CONTRACT_DELETE] ON [CONTRACT]

FOR DELETE

AS

BEGIN

DELETE FROM CONTRACTDETAIL

WHERE CONTRACTID in (SELECT CONTRACTID FROM deleted)

END

 

INSTEAD OF 触发器     

用INSTEAD OF 可以指定执行触发器而不是执行触发 SQL 语句,从而屏蔽原来的SQL语句,而转向执行触发器内部的SQL语句。每一个触发行为只能定义一个INSTEAD OF触发器。

 

例7:合同明细表是合同表的从表,当对合同表进行删除操作时,可以检测合同明细表中是否有相关记录,如果有,则禁止删除。

CREATE TRIGGER [CONTRACT_DELETE] ON [CONTRACT]

INSTEAD OF DELETE

AS

BEGIN

IF EXISTS

(SELECT Count(*) FROM ContractDetail

WHERE ContractID IN (SELECT ContractID FROM Deleted))

PRINT ‘合同明细表中有相关记录,不能删除’

ELSE

DELETE FROM CONTRACT

WHERE CONTRACTID in (SELECT CONTRACTID FROM deleted)

END

 

WITH ENCRYPTION

       加密 syscomments 表中包含 CREATE TRIGGER 语句文本的条目。使用 WITH ENCRYPTION 可防止将触发器作为 SQL Server 复制的一部分发布。

AFTER

      指定触发器只有在触发 SQL 语句中指定的所有操作都已成功执行后才激发。所有的引用级联操作和约束检查也必须成功完成后,才能执行此触发器。

  如果仅指定 FOR 关键字,则 AFTER 是默认设置。不能在视图上定义 AFTER 触发器。

 

嵌套触发器

       触发器可以嵌套使用,最多可以嵌套 32 层。如果一个触发器更改了包含另一个触发器的表,则第二个触发器将激活,然后该触发器可以再调用第三个触发器,依此类推

 

递规触发器    

       有两种类型的递归触发器:间接递归和直接递归。使用间接递归时,应用程序更新表 T1,从而激发触发器 TR1,该触发器更新表 T2。在这种情况下,触发器 T2 将激发并更新 T1。

使用直接递归时,应用程序更新表 T1,从而激发触发器 TR1,该触发器更新表 T1。由于表 T1 被更新,触发器 TR1 再次激发,依此类推。使用递归触发器,要保证不会出现死循环。

 

使用触发器和存储过程必须注意的地方

触发器和存储过程为开发数据库应用带来了很大的方便,但是在使用他们时一定要注意一些限制,否则会带来负面的影响,为应用程序带来不可预见的错误。

 

重新编译存储过程和触发器

存储过程和触发器所用的查询只在编译时进行优化。对数据库进行了索引或其它会影响数据库统计的更改后,已编译的存储过程和触发器可能会失去效率。通过对作用于表上的存储过程和触发器进行重新编译,可以重新优化查询。

 

使用存储过程的注意事项

使用存储过程,需要注意以下几点:
用户定义的存储过程只能在当前数据库中创建(临时过程除外,临时过程总是在 tempdb 中创建)

SQL Server 允许创建的存储过程引用尚不存在的对象。在创建时,只进行语法检查。

SQL Server 支持两种临时过程:局部临时过程和全局临时过程。局部临时过程只能由创建该过程的连接使用。全局临时过程则可由所有连接使用。

除了 SET SHOWPLAN_TEXT 和 SET SHOWPLAN_ALL 之外(这两个语句必须是批处理中仅有的语句),任何 SET 语句均可以在存储过程内部指定。

 

触发器的T_SQL限制

    使用触发器时,请注意有如下限制:

CREATE TRIGGER 必须是批处理中的第一条语句,并且只能应用到一个表中。

触发器只能在当前的数据库中创建,不过触发器可以引用当前数据库的外部对象。

如果指定触发器所有者名称以限定触发器,请以相同的方式限定表名。

 

在同一条 CREATE TRIGGER 语句中,可以为多种用户操作(如 INSERT 和 UPDATE)定义相同的触发器操作。

如果一个表的外键在 DELETE/UPDATE 操作上定义了级联,则不能在该表上定义 INSTEAD OF DELETE/UPDATE 触发器。

在触发器内可以指定任意的 SET 语句。所选择的 SET 选项在触发器执行期间有效,并在触发器执行完后恢复到以前的设置。

 

与使用存储过程一样,当触发器激发时,将向调用应用程序返回结果。若要避免由于触发器激发而向应用程序返回结果,请不要包含返回结果的 SELECT 语句,也不要包含在触发器中进行变量赋值的语句。

包含向用户返回结果的 SELECT 语句或进行变量赋值的语句的触发器需要特殊处理;这些返回的结果必须写入允许修改触发器表的每个应用程序中。

如果必须在触发器中进行变量赋值,则应该在触发器的开头使用 SET NOCOUNT 语句以避免返回任何结果集。

 

DELETE 触发器不能捕获 TRUNCATE TABLE 语句。尽管 TRUNCATE TABLE 语句实际上是没有 WHERE 子句的 DELETE(它删除所有行),但它是无日志记录的,因而不能执行触发器。因为 TRUNCATE TABLE 语句的权限默认授予表所有者且不可转让,所以只有表所有者才需要考虑无意中用 TRUNCATE TABLE 语句规避 DELETE 触发器的问题。

无论有日志记录还是无日志记录,WRITETEXT 语句都不激活触发器。

触发器中不允许以下 Transact-SQL 语句:ALTER DATABASE 、CREATE DATABASE、 DISK INIT 、DISK RESIZE 、DROP DATABASE 、LOAD DATABASE 、LOAD LOG 、RECONFIGURE 、RESTORE DATABASE 、RESTORE LOG

猜你喜欢

转载自blog.csdn.net/shujian_tianya/article/details/81151798