邹平二手房zp99fc:数据库触发器 - chinayuan的专栏 - CSDN博客

来源:百度文库 编辑:九乡新闻网 时间:2024/04/29 18:50:07

Oracle 触发器 :
触发器是特定事件出现的时候,自动执行的代码块。类似于存储过程,但是用户不能直接调用他们。
功能:

1 、 允许 / 限制对表的修改

2 、 自动生成派生列,比如自增字段

3 、 强制数据一致性

4 、 提供审计和日志记录

5 、 防止无效的事务处理

6 、 启用复杂的业务逻辑
开始 :

create trigger biufer_employees_department_id

  before insert or update

  of department_id

  on employees

  referencing old as old_value

     new as new_value

  for each row

  when (new_value.department_id<>80 )

begin

  :new_value.commission_pct :=0;

end;

/

触发器的组成部分:

1 、 触发器名称

2 、 触发语句

3 、 触发器限制

4 、 触发操作
 

1 、 触发器名称

create trigger biufer_employees_department_id

命名习惯:

biufer ( before insert update for each row )

employees 表名

department_id 列名
 

2 、 触发语句

比如:

表或视图上的 DML 语句; DDL 语句,数据库关闭或启动 ,startup shutdown 等等

before insert or update

  of department_id

  on employees

  referencing old as old_value

     new as new_value

  for each row

说明:

( 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_value.commission_pct :=0;

end;

主体很简单,就是将更新后的 commission_pct 列置为 0

 

触发:

insert into employees
(employee_id,last_name,first_name,hire_date,job_id,email,department_id,salary,commission_pct )
values( 12345,’Chen’,’Donny’, sysdate, 12, ‘donny@hotmail.com’,60,10000,.25);

 

select commission_pct from employees where employee_id=12345;

触发器不会通知用户,便改变了用户的输入值。
 

触发器类型:

1 、 语句触发器

2 、 行触发器

3 、 INSTEAD OF 触发器

4 、 系统条件触发器

5 、 用户事件触发器
 

1 、 语句触发器
是在表上或者某些情况下的视图上执行的特定语句或者语句组上的触发器。能够与 INSERT 、 UPDATE 、 DELETE 或者组合上进行关联。但是无论使用什么样的组合,各个语句触发器都只会针对指定语句激活一次。 比如,无论 update 多少行,也只会调用一次 update 语句触发器。
例子:

需要对在表上进行 DML 操作的用户进行安全检查,看是否具有合适的特权。

Create table foo(a number);

Create trigger biud_foo

  Before insert or update or delete

  On foo

Begin

  If user not in (‘DONNY’) then

  Raise_application_error(-20001, ‘You don’t have access to modify this table.’);

  End if;

End;

/

即使 SYS , SYSTEM 用户也不能修改 foo 表
[ 试验 ]

对修改表的时间、人物进行日志记录。
1 、 建立试验表

create table employees_copy as select *from hr.employees

2 、 建立日志表

create table employees_log( who varchar2(30), when date);

3 、 在 employees_copy 表上建立语句触发器,在触发器中填充 employees_log 表。

Create or replace trigger biud_employee_copy

  Before insert or update or delete

   On employees_copy

  Begin

  Insert into employees_log( Who,when)   Values( user, sysdate);

  End;

  /

4 、 测试

update employees_copy set salary= salary*1.1;

select *from employess_log;

5 、 确定是哪个语句起作用?

即是 INSERT/UPDATE/DELETE 中的哪一个触发了触发器?

可以在触发器中使用 INSERTING / UPDATING / DELETING 条件谓词 ,作判断:

begin

  if inserting then

   -----

  elsif updating then

   -----

  elsif deleting then

   ------

  end if;

end;

if updating(‘COL1’) or updating(‘COL2’) then

  ------

end if;

[ 试验 ]

1 、 修改日志表

alter table employees_log add (action varchar2(20));

2 、 修改触发器,以便记录语句类型。

Create or replace trigger biud_employee_copy

  Before insert or update or delete

   On employees_copy

  Declare

  L_action employees_log.action%type;

  Begin

  if inserting then

   l_action:=’Insert’;

  elsif updating then

   l_action:=’Update’;

  elsif deleting then

   l_action:=’Delete’;

  else

   raise_application_error(-20001,’You should never ever get this error.’);

  Insert into employees_log( Who,action,when)   Values( user, l_action,sysdate);

  End;

  /

3 、 测试

insert into employees_copy( employee_id, last_name, email, hire_date, job_id)

  values(12345,’Chen’,’Donny@hotmail’,sysdate,12);

select *from employees_log

update employees_copy set salary=50000 where employee_id = 12345;

2 、 行触发器
是指为受到影响的各个行激活的触发器,定义与语句触发器类似,有以下两个例外:

1 、 定义语句中包含 FOR EACH ROW 子句

2 、 在 BEFORE …… FOR EACH ROW 触发器中,用户可以引用受到影响的行值。

比如:
定义:

create trigger biufer_employees_department_id

  before insert or update

  of department_id

  on employees_copy

  referencing old as old_value

     new as new_value

  for each row

  when (new_value.department_id<>80 )

begin

  :new_value.commission_pct :=0;

end;

/

Referencing 子句:

执行 DML 语句之前的值的默认名称是 :old , 之后的值是 :new

insert 操作只有 :new

delete 操作只有 :old

       update 操作两者都有
referencing 子句只是将 new 和 old 重命名为 new_value 和 old_value ,目的是避免混淆。比如操作一个名为 new 的表时。作用不很大。
 

[ 试验 ] :为主健生成自增序列号
drop table foo;

create table foo(id number, data varchar2(20));

create sequence foo_seq;

create or replace trigger bifer_foo_id_pk

  before insert on foo

  for each row

begin

  select foo_seq.nextval into :new.id from dual;

end;

/

insert into foo(data) values(‘donny’);

insert into foo values(5,’Chen’);

select * from foo;

3 、 INSTEAD OF 触发器更新视图 instead of 触发器,可以实现 : 不执行导致 trigger 触发的语句,而只执行触发器 .

INSTEAD OF triggers provide a transparent way of modifying views that cannot be modified directly through DML statements (INSERT, UPDATE, and DELETE). These triggers are called INSTEAD OF triggers because, unlike other types of triggers, Oracle fires the trigger instead of executing the triggering statement. You can write normal INSERT, UPDATE, and DELETE statements against the view and the INSTEAD OF trigger is fired to update the underlying tables appropriately. INSTEAD OF triggers are activated for each row of the view that gets modified.

 

Create or replace view company_phone_book as

  Select first_name||’, ’||last_name name, email, phone_number,employee_id emp_id

  From hr.employees;

尝试更新 email 和 name

update hr.company_phone_book set name=’Chen1, Donny1’ where emp_id=100

create or replace trigger update_name_company_phone_book

INSTEAD OF

Update on hr.company_phone_book

Begin

  Update hr.employees

  Set employee_id=:new.emp_id,

   First_name=substr(:new.name, instr(:new.name,’,’)+2),

   last_name= substr(:new.name,1,instr(:new.name,’,’)-1),

   phone_number=:new.phone_number,

   email=:new.email

  where employee_id=:old.emp_id;

end;

 

instead of trigger 是基于视图建立的 , 不能建在表上 , 为什么要建在视图上 , 一般的视图如果其数据来源一个表并且包含该表的主键 , 就可以对视图进行 DML 操作 . 另外一种情况是从多个表查询出来的 . 这样我们就不能对视图进行操作了 , 也就是只能查询 .instead of trigger 可以解决建在多表上视图的更新操作 .

 

下面我们就来实例操作 :

a. 先建表 , 简单点就三个分别是学生表 , 课程表 , 学生选课表

CREATE TABLE STUDENT

(

   CODE  VARCHAR2(5),

   LNAME VARCHAR2(200)

)

CREATE TABLE COURSE

(

    CODE  VARCHAR2(5),

    CNAME VARCHAR2(30)

)

CREATE TABLE ST_CR

(

   STUDENT VARCHAR2(5),

   COURSE  VARCHAR2(5),

   GRADE   NUMBER

)

-- 表的约束

ALTER TABLE STUDENT ADD CONSTRAINT STUDENT$PK PRIMARY KEY(CODE);

ALTER TABLE COURSE ADD CONSTRAINT COURSE$PK PRIMARY KEY(CODE);

ALTER TABLE ST_CR ADD CONSTRAINT ST_CR$PK PRIMARY KEY(STUDENT, COURSE);

ALTER TABLE ST_CR ADD CONSTRAINT ST_CR$FK$STUDENT FOREIGN KEY(STUDENT) REFERENCES STUDENT(CODE);

ALTER TABLE ST_CR ADD CONSTRAINT ST_CR$FK$COURSE FOREIGN KEY(COURSE) REFERENCES COURSE(CODE);

 

b. 基于这三个表的视图

CREATE OR REPLACE VIEW STUDENT_STATUS AS

   SELECT S.CODE S_CODE, S.LNAME STUDENT, C.CODE C_CODE, C.CNAME COURSE, SC.GRADE GRADE

   FROM STUDENT S, COURSE C, ST_CR SC

   WHERE S.CODE = SC.STUDENT

          AND C.CODE = SC.COURSE

 

c. 基于视图的触发器

CREATE OR REPLACE TRIGGER TRI_STCR INSTEAD OF INSERT ON STUDENT_STATUS

   FOR EACH ROW

DECLARE

  W_ACTION VARCHAR2(1);

BEGIN

  IF    INSERTING THEN

    W_ACTION := 'I';

  ELSE

    RAISE PROGRAM_ERROR;

  END IF;

    INSERT INTO STUDENT(CODE, LNAME) VALUES(:NEW.S_CODE,:NEW.STUDENT);

    INSERT INTO COURSE(CODE, CNAME) VALUES(:NEW.C_CODE, :NEW.COURSE);

    INSERT INTO ST_CR(STUDENT, COURSE, GRADE)

       VALUES(:NEW.S_CODE, :NEW.C_CODE, :NEW.GRADE);

END;

 

d. 对视图执行数据插入

INSERT INTO STUDENT_STATUS(S_CODE, STUDENT, C_CODE, COURSE, GRADE)

VALUES('001','Mike','EN','English',86);

可以看到每个表各有一条数据已经插入 .

4 、 系统事件触发器
系统事件:数据库启动、关闭,服务器错误
create trigger ad_startup

  after startup

  on database

begin

  -- do some stuff

end;

/

5 、 用户事件触发器
用户事件:用户登陆、注销, CREATE / ALTER / DROP / ANALYZE / AUDIT / GRANT / REVOKE / RENAME / TRUNCATE / LOGOFF

例子:记录删除对象
1. 日志表

create table droped_objects(

  object_name varchar2(30),

  object_type varchar2(30),

  dropped_on date);

2 .触发器

create or replace trigger log_drop_trigger

  before drop on donny.schema

begin

  insert into droped_objects values(

  ora_dict_obj_name,  -- 与触发器相关的函数

  ora_dict_obj_type,

  sysdate);

end;

/

 

3. 测试

create table drop_me(a number);

create view drop_me_view as select *from drop_me;

drop view drop_me_view;

drop table drop_me;

select *from droped_objects

 

禁用和启用触发器
alter trigger disable;

alter trigger enable;

事务处理:
在触发器中,不能使用 commit / rollback, 因为 ddl 语句具有隐式的 commit ,所以也不允许使用

视图:
dba_triggers

MS SQLServer 触发器 :
SQL Sever 2005 包含的 3 个触发器对象 :

       AFTER ,数据定义语言 (DDL) 和 INSTEAD-OF

1.  AFTER 触发器是存储程序,它发生于数据操作语句作用之后,例如删除语句等。

2.  DDL 是 SQL Server 2005 的新触发器,允许响应数据库引擎中对象定义水平事件 ( 例如 :DROP TABLE 语句 ) 。

3.  INSTEAD-OF 触发器是对象,在数据库引擎中可以取代数据操作语句而执行。例如 : 将 INSTEAD-OF INSERT 触发器附加到表,告诉数据库执行此触发器

 

SQL Server 2005 中 DDL 触发器的实现
SQL SERVER 2005 中,新增加了许多新的特性,其中的 DDL 触发器是个不错的选择,根据资料初步学习如下,现整理之:
   在 sql server 2000 中,只能为针对表发出的 DML 语句( INSERT 、 UPDATE 和 DELETE )定义 AFTER 触发器。 SQL Server 2005 可以就整个服务器或数据库的某个范围为 DDL 事件定义触发器。可以为单个 DDL 语句(例如, CREATE_TABLE )或者为一组语句(例如, DDL_DATABASE_LEVEL_EVENTS )定义 DDL 触发器。在该触发器内部,您可以通过访问 eventdata() 函数获得与激发该触发器的事件有关的数据。该函数返回有关事件的 XML 数据。每个事件的架构都继承了 Server Events 基础架构。

    比如,在 SQL SERVER 2005 中,建立一个叫 DDLTrTest 的数据库,并且建立一个叫 mytable 的表和 Usp_Querymytable 的存储过程,如下所示

 

DROP DATABASE [DDLTRTEST]

GO

CREATE DATABASE DDLTRTEST

GO

USE [DDLTRTEST]

GO

IF EXISTS (SELECT * FROM SYS.OBJECTS WHERE OBJECT_ID = OBJECT_ID(N'[DBO].[MYTABLE]') AND TYPE IN (N'U'))

DROP TABLE [DBO].[MYTABLE]

GO

CREATE TABLE MYTABLE(ID INT, NAME VARCHAR(100))

GO

INSERT INTO MYTABLE SELECT 1,'A'

INSERT INTO MYTABLE SELECT 2,'B'

INSERT INTO MYTABLE SELECT 3,'C'

INSERT INTO MYTABLE SELECT 4,'D'

INSERT INTO MYTABLE SELECT 5,'E'

INSERT INTO MYTABLE SELECT 6,'F'

GO

USE [DDLTrTest]

GO

IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[usp_querymytable]') AND type in (N'P', N'PC'))

DROP PROCEDURE [dbo].[usp_querymytable]

GO

CREATE PROC USP_QUERYMYTABLE AS SELECT * FROM MYTABLE

GO

 

接下来定义一个 DDL 触发器如下 :

CREATE TRIGGER STOP_DDL_on_Table_and_PROC

ON DATABASE

FOR

CREATE_TABLE, DROP_TABLE, ALTER_TABLE,

CREATE_PROCEDURE, ALTER_PROCEDURE,DROP_PROCEDURE

AS

SELECT EVENTDATA().value('(/EVENT_INSTANCE/TSQLCommand/CommandText)[1]', 'nvarchar(max)')

PRINT 'You are not allowed to CREATE,ALTER and DROP any Tables and Procedures'

ROLLBACK;

接下来,我们尝试如下的操作:

ALTER TABLE MYTABLE ADD X INT

 

结果如下,出现错误提示

ALTER TABLE MYTABLE ADD X INT

(1 row(s) affected)

You are not allowed to CREATE,ALTER and DROP any Tables and Procedures

Msg 3609, Level 16, State 2, Line 1

The transaction ended in the trigger. The batch has been aborted.

 

再执行 DROP 的操作,同样触发警告

DROP TABLE MYTABLE

(1 row(s) affected)

You are not allowed to CREATE,ALTER and DROP any Tables and Procedures

Msg 3609, Level 16, State 2, Line 1

The transaction ended in the trigger. The batch has been aborted.


因为我们的触发器规定了不能使用 CREATE_TABLE,DROP_TABLE, ALTER_TABLE,CREATE_PROCEDURE,ALTER_PROCEDURE,DROP_PROCEDURE 等操作。

如果我们要关掉这个触发器,可以这样做:  

DISABLE TRIGGER STOP_DDL_ON_TABLE_AND_PROC ON DATABASE

当然,我们要对整个服务器采取策略的话,也是很简单的,和上面的方法大致相同只不过将 on database 的参数改为 on server, 比如

 

CREATE TRIGGER STOP_DDL_on_Table_and_PROC

ON ALL SERVER

FOR

CREATE_DATABASE,ALTER_DATABASE,DROP_DATABASE

AS

PRINT 'You are not allowed to CREATE,ALTER and DROP any Databases'

ROLLBACK;

 

如何使用 SQL Server 2005 INSTEAD OF 触发器
触发器是类似于存储程序的数据库对象,它响应数据库环境下的某个请求。 SQL Sever 2005 包含 3 个触发器对象: AFTER ,数据定义语言 (DDL) 和 INSTEAD-OF 。

AFTER 触发器是存储程序,它发生于数据操作语句作用之后,例如删除语句等。 DDL 是 SQL Server 2005 的新触发器,允许响应数据库引擎中对象定义水平事件(例如: DROP TABLE 语句)。 INSTEAD-OF 触发器是对象,在数据库引擎中可以取代数据操作语句而执行。例如:将 INSTEAD-OF INSERT 触发器附加到表,告诉数据库执行此触发器。

1.       使用 INSTEAD-OF 触发器的理由

INSTEAD-OF 触发器是 SQL Sever 功能强大的对象,允许开发人员转移数据库引擎完成不同的工作,以满足开发要求。其中的一个例子是在数据库的表中添加 INSTEAD-OF 触发器,当不需要修改表时,可以对表的内容进行回滚。使用此方法时,必须格外小心,因为任何指定的表修改之前, INSTEAD-OF 触发器必须处于激活状态。

使用 INSTEAD-OF 触发器一个更充分理由是视图处理。在视图中添加 INSTEAD-OF 触发器后,则可创建更新的视图。可更新视图允许完整地提取数据库大纲,因此可以用此方法设计系统,而不需要担心 OLTP 数据库大纲的问题,并且取代数据修改一组标准视图集。
2.       范例

为了更好地说明可更新视图概念,我们提供一个示例。在本例中,我们设计一个产品表(记录产品),一个购买表(记录购买)。 Listing A 包含了创建表的脚本,运行此脚本后则得到示例中所要用到的表。运行 Listing B 脚本向表中添加数据。

现在表中已经有数据了,我可以为这些表创建一些有意义的视图。请查看 Listing C 。

这是个典型的产品水平的视图。它联合了数据库中的两个表,使得数据简单化了。但是,对于数据提取,使用视图则没有什么优势。在视图上附上 INSTEAD- OF 触发器后,则允许修改表,但是我不需要直接修改表中的数据。我使用 Listing D 中的代码在 vw_ProductPurchases 视图上创建一个 INSTEAD-OF 触发器。

请注意此 INSTEAD OF 触发器的声明。 SQL Server 创建的默认的触发器为 AFTER 触发器,因此,必须在触发器定义中指定 INSTEAD OF 子句。

触发器的第一条语句是“ check ”语句。本例中我使用此语句检测 INSERTED 表以确保显示 ProductID 字段,并且保证提供显示其他 PurchasePrice 或 ProductPrice 字段。

如果必要的数据通过 INSERT 语句都插入到视图中,则触发器将插入指定的值到数据表。下面即是视图的 INSERT 语句。

INSERT INTO vw_ProductPurchases(ProductID, PurchasePrice) VALUES(1, 700)

INSERT 语句提供了有效的 ProductID 和 PurchasePrice ,这意味着新记录插入到 Purchases 表

3. 结论

INSTEAD-OF 触发器具有强大的功能和灵活性。如果系统不大,使用视图系统提取数据大纲能够极大保护数据库程序。上面的例子很简单,系统中所用到的复杂的触发器需要考虑安全性问题、时间开销和其他限制。

 

MSSQLServer 触发器介绍:
触发器实际上就是一种特殊类型的存储过程,其特殊性表现在:它是在执行某些特定的 T-SQL 语句时自动的。

11.1  触发器简介

触发器实际上就是一种特殊类型的存储过程,它是在执行某些特定的 T-SQL 语句时自动执行的一种存储过程。在 SQL Server 2005 中,根据 SQL 语句的不同,把触发器分为两类:一类是 DML 触发器,一类是 DLL 触发器。

 

11.1.1   触发器的概念和作用

在 SQL Server 2005 里,可以用两种方法来保证数据的有效性和完整性:约束( check )和触发器( Trigger )。 约束是直接设置于数据表内,只能现实一些比较简单的功能操作,如:实现字段有效性和唯一性的检查、自动填入默认值、确保字段数据不重复(即主键)、确保数据表对应的完整性(即外键)等功能。

触发器是针对数据表(库)的特殊的存储过程,当这个表发生了 Insert 、 Update 或 Delete 操作时,会自动激活执行的,可以处理各种复杂的操作。在 SQL Server 2005 中,触发器有了更进一步的功能,在数据表(库)发生 Create 、 Alter 和 Drop 操作时,也会自动激活执行。

触发器常用的一些功能如下:

l         完成比约束更复杂的数据约束:触发器可以实现比约束更为复杂的数据约束

l         检查所做的 SQL 是否允许:触发器可以检查 SQL 所做的操作是否被允许。例如:在产品库存表里,如果要删除一条产品记录,在删除记录时,触发器可以检查该产品库存数量是否为零,如果不为零则取消该删除操作。

l         修改其它数据表里的数据:当一个 SQL 语句对数据表进行操作的时候,触发器可以根据该 SQL 语句的操作情况来对另一个数据表进行操作。例如:一个订单取消的时候,那么触发器可以自动修改产品库存表,在订购量的字段上减去被取消订单的订购数量。

l         调用更多的存储过程:约束的本身是不能调用存储过程的,但是触发器本身就是一种存储过程,而存储过程是可以嵌套使用的,所以触发器也可以调用一个或多过存储过程。

l         发送 SQL Mail :在 SQL 语句执行完之后,触发器可以判断更改过的记录是否达到一定条件,如果达到这个条件的话,触发器可以自动调用 SQL Mail 来发送邮件。例如:当一个订单交费之后,可以物流人员发送 Email ,通知他尽快发货。

l         返回自定义的错误信息:约束是不能返回信息的,而触发器可以。例如插入一条重复记录时,可以返回一个具体的友好的错误信息给前台应用程序。

l         更改原本要操作的 SQL 语句:触发器可以修改原本要操作的 SQL 语句,例如原本的 SQL 语句是要删除数据表里的记录,但该数据表里的记录是最要记录,不允许删除的,那么触发器可以不执行该语句。

l         防止数据表构结更改或数据表被删除:为了保护已经建好的数据表,触发器可以在接收到 Drop 和 Alter 开头的 SQL 语句里,不进行对数据表的操作。

 

11.1.2   触发器的种类

在 SQL Server 2005 中,触发器可以分为两大类: DML 触发器和 DDL 触发器

l         DML 触发器: DML 触发器是当数据库服务器中发生数据操作语言( Data Manipulation Language )事件时执行的存储过程。 DML 触发器又分为两类: After 触发器和 Instead Of 触发器

l         DDL 触发器: DDL 触发器是在响应数据定义语言( Data Definition Language )事件时执行的存储过程。 DDL 触发器一般用于执行数据库中管理任务。如审核和规范数据库操作、防止数据库表结构被修改等。

11.2  DML 触发器的分类

SQL Server 2005 的 DML 触发器分为两类:
l         After 触发器:这类触发器是在记录已经改变完之后( after ),才会被激活执行,它主要是用于记录变更后的处理或检查,一旦发现错误,也可以用 Rollback Transaction 语句来回滚本次的操作。

l         Instead Of 触发器:这类触发器一般是用来取代原本的操作,在记录变更之前发生的,它并不去执行原来 SQL 语句里的操作( Insert 、 Update 、 Delete ),而去执行触发器本身所定义的操作。

11.3  DML 触发器的工作原理

在 SQL Server 2005 里,为每个 DML 触发器都定义了两个特殊的表,一个是插入表,一个是删除表。这两个表是建在数据库服务器的内存中的,是由系统管理的逻辑表,而不是真正存储在数据库中的物理表。对于这两个表,用户只有读取的权限,没有修改的权限。

这两个表的结构与触发器所在数据表的结构是完全一致的,当触发器的工作完成之后,这两个表也将会从内存中删除。

插入表里存放的是更新前的记录:对于插入记录操作来说,插入表里存放的是要插入的数据;对于更新记录操作来说,插入表里存放的是要更新的记录。

删除表里存放的是更新后的记录:对于更新记录操作来说,删除表里存放的是更新前的记录(更新完后即被删除);对于删除记录操作来说,删除表里存入的是被删除的旧记录。

下面看一下触发器的工作原理。

11.3.1   After 触发器的工作原理

After 触发器是在记录更变完之后才被激活执行的。以删除记录为例:当 SQL Server 接收到一个要执行删除操作的 SQL 语句时, SQL Server 先将要删除的记录存放在删除表里,然后把数据表里的记录删除,再激活 After 触发器,执行 After 触发器里的 SQL 语句。执行完毕之后, 删除内存中的删除表,退出整个操作。

还是举上面的例子:在产品库存表里,如果要删除一条产品记录,在删除记录时,触发器可以检查该产品库存数量是否为零,如果不为零则取消删除操作。看一下数据库是怎么操作的:

( 1 )接收 SQL 语句,将要从产品库存表里删除的产品记录取出来,放在删除表里。

( 2 )从产品库存表里删除该产品记录。

( 3 )从删除表里读出该产品的库存数量字段,判断是不是为零,如果为零的话,完成操作,从内存里清除删除表;如果不为零的话,用 Rollback Transaction 语句来回滚操作。

11.3.2   Instead Of 触发器的工作原理

Instead Of 触发器与 After 触发器不同。 After 触发器是在 Insert 、 Update 和 Delete 操作完成后才激活的,而 Instead Of 触发器,是在这些操作进行之前就激活了,并且不再去执行原来的 SQL 操作,而去运行触发器本身的 SQL 语句。

11.4  设计 DML 触发器的注意事项及技巧

在了解触发器的种类和工作理由之后,现在可以开始动手来设计触发器了,不过在动手之前,还有一些注意事项必须先了解一下:
11.4.1   设计触发器的限制

在触发器中,有一些 SQL 语句是不能使用的,这些语句包括:

表 11.1  在 DML 触发器中不能使用的语句

不能使用的语句
 语句功能
 
Alter Database
 修改数据库
 
Create Database
 新建数据库
 
Drop Database
 删除数据库
 
Load Database
 导入数据库
 
Load Log
 导入日志
 
Reconfigure
 更新配置选项
 
Restore Database
 还原数据库
 
Restore Log
 还原数据库日志
 

另外,在对作为触发操作的目标的表或视图使用了下面的 SQL 语句时,不允许在 DML 触发器里再使用这些语句:

表 11.2 在目标表中使用过的, DML 触发器不能再使用的语句

不能使用的语句
 语句功能
 
Create Index
 建立索引
 
Alter Index
 修改索引
 
Drop Index
 删除索引
 
DBCC Dbreindex
 重新生成索引
 
Alter Partition Function
 通过拆分或合并边界值更改分区
 
Drop Table
 删除数据表
 
Alter Table
 修改数据表结构
 

11.4.2   如何在触发器取得字段修改前和修改后的数据

上面介绍过, SQL Server 2005 在为每个触发器都定义了两个虚拟表,一个是插入表( inserted ),一个是删除表( deleted ),现在把这两个表存放的数据列表说明一下:

表 11.3  插入 / 删除表的功能

激活触发器的动作
 Inserted 表
 Deleted 表
 
Insert
 存放要插入的记录
  
 
Update
 存放要更新的记录
 存放更新前的旧记录
 
Delete
  
 存放要删除的旧记录
 

以上面删除库存产品记录为例,在删除时触发器要判断库存数量是否为零,那么判断就应该这么写:

If (Select 库存数量 From Deleted)>0

Begin

Print ‘ 库存数量大于零时不能删除此记录 ’

Rollback Transaction

End

11.4.3   其他注意事项

l         l  After 触发器只能用于数据表中, Instead Of 触发器可以用于数据表和视图上,但两种触发器都不可以建立在临时表上。

l         l  一个数据表可以有多个触发器,但是一个触发器只能对应一个表。

l         l  在同一个数据表中,对每个操作(如 Insert 、 Update 、 Delete )而言可以建立许多个 After 触发器,但 Instead Of 触发器针对每个操作只有建立一个。

l         l  如果针对某个操作即设置了 After 触发器又设置了 Instead Of 触发器,那么 Instead of 触发器一定会激活,而 After 触发器就不一定会激活了。

l         l  Truncate Table 语句虽然类似于 Delete 语句可以删除记录,但是它不能激活 Delete 类型的触发器。因为 Truncate Table 语句是不记入日志的。

l         l  WRITETEXT 语句不能触发 Insert 和 Update 型的触发器。

l         l  不同的 SQL 语句,可以触发同一个触发器,如 Insert 和 Update 语句都可以激活同一个触发器。

11.5  设计 After 触发器

在了解触发器及其种类、作用、工作原理之后,下面详细讲述一下要怎么去设计及建立触发器。

11.5.1   设计简单的 After 触发器

下面用实例设计一个简单的 After Insert 触发器,这个触发器的作用是:在插入一条记录的时候,发出 “ 又添加了一种产品 ” 的友好提示。

( 1 )启动 Management Studio ,登录到指定的服务器上。

( 2 )在如图 11.1 所示界面的【对象资源管理器】下选择【数据库】,定位到【 Northwind 】数据库 à 【表】 à 【 dbo. 产品】,并找到【触发器】项。

 

图 11.1 定位到触发器

( 3 )右击【触发器】,在弹出的快捷菜单中选择【新建触发器】选项,此时会自动弹出【查询编辑器】对话框,在【查询编辑器】的编辑区里 SQL Server 已经预写入了一些建立触发器相关的 SQL 语句,如图 11.2 所示。

 

图 11.2 SQL Server 2005 预写的触发器代码

( 4 )修改【查询编辑器】里的代码,将从 “CREATE” 开始到 “GO” 结束的代码改为以下代码:

CREATE TRIGGER 产品 _Insert

   ON  产品

   AFTER INSERT

AS

BEGIN

         print ' 又添加了一种产品 '

END

GO

如果有兴趣的话,也可以去修改一下如图 11.2 中绿色部分的版权信息。

( 5 )单击工具栏中的【分析】按钮  ,检查一下是否语法有错,如图 11.3 所示,如果在下面的【结果】对话框中出现 “ 命令已成功完成 ” ,则表示语法没有错误。

 

图 11.3 检查语法

( 6 )语法检查无误后,单击【执行】按钮,生成触发器。

( 7 )关掉查询编辑器对话框,刷新一下触发器对话框,可以看到刚才建立的【产品 _Insert 】触发器,如图 11.4 所示。

 

图 11.4 建好的触发器

建立 After Update 触发器、 After Delete 触发器和建立 After Insert 触发器的步骤一致,不同的地方是把上面的 SQL 语句中的 AFTER INSERT 分别改为 AFTER UPDATE 和 AFTER DELETE 即可,如下所示,有兴趣的读者可以自行测试。

CREATE TRIGGER 产品 _Update

   ON  产品

   AFTER UPDATE

AS

BEGIN

         print ' 有一种产品更改了 '

END

GO

CREATE TRIGGER 产品 _Delete

   ON  产品

   AFTER DELETE

AS

BEGIN

         print ' 又删除了一种产品 '

END

GO

11.5.2   测试触发器功能

建好 After Insert 触发器之后,现在来测试一下触发器是怎么样被激活的。

( 1 )在 Management Studio 里新建一个查询,在弹出的【查询编辑器】对话框里输入以下代码:

INSERT INTO 产品 ( 产品名称 ) VALUES (' 大苹果 ')

( 2 )单击【执行】按钮,可以看到【消息】对话框里显示出一句提示: “ 又添加了一种产品 ” ,如图 11.5 所示,这说明, After Insert 触发器被激活,并运行成功了。

 

图 11.5 查看触发器的运行结果

而如果在【查询编辑器】里执行的不是一个 Insert 语句,而是一个 Delete 语句的话, After Insert 触发器将不会被激活。如在【查询编辑器】输入以下语句:

DELETE FROM 产品 WHERE ( 产品名称 = ' 大苹果 ')

单击【执行】按钮,在【消息】对话框里只显示了一句 “(1 行受影 响 )” 的提示,而没有 “ 又添加了一种产品 ” 的提示,如图 11.6 所示。这是因为 Delete 语句是不能激活 After Insert 触发器,所以 After Insert 触发器里的 “print ‘ 又添加了一种产品 ’” 语句并没有执行。

 

图 11.6 执行删除语句不会激活 After Insert 触发器

11.5.3   建立触发器的 SQL 语句

回顾一下,在 Management Studio 新建一个触发器的时候,它在查询分析对话框给预设了一些 SQL 代码,这些代码其实上就是建立触发器的语法提示。现在来看一下完整的触发器语法代码:

CREATE TRIGGER .

   ON  .

   AFTER

AS

BEGIN

         -- SET NOCOUNT ON added to prevent extra result sets from

         -- interfering with SELECT statements.

         SET NOCOUNT ON;

    -- Insert statements for trigger here

END

GO

用中文改了一下,以上代码就一目了然了:

CREATE TRIGGER 触发器名

   ON  数据表名或视图名

   AFTER INSERT 或 DELETE 或 UPDATE

AS

BEGIN

         -- 这里是要运行的 SQL 语句

END

GO

现在再对上面的代码进行进一步的说明:

l         l  CREATE TRIGGER 触发器名:这一句声明 SQL 语句是用来建立一个触发器。其中触发器名在所在的数据库里必须是唯一的。由于触发器是建立中数据表或视图中的,所以有很多人都 以为只要是在不同的数据表中,触发器的名称就可以相同,其实触发器的全名( Server.Database.Owner.TriggerName )是必须 唯一的,这与触发器在哪个数据表或视图无关。

l         l  ON 数据表名或视图名:这是指定触发器所在的数据表或视图,但是请注意,只有 Instead Of 触发器才能建立在视图上。并且,有设置为 With Check Option 的视图也不允许建立 Instead Of 触发器。

l         l  AFTER INSERT 或 DELETE UPDATE :这是指定触发器的类型,是 After Insert 触发器,还是 After Delete 触发器,或者是 After Update 触发器。其中 After 可以用 For 来代取,它们的意思都是一样的,代表只有在数据表的操作都已正确完成后才会激活的触发器。 INSERT 、 DELETE 和 UPDATE 至少要指定一个,当然也可以指定多个,若指定多个时,必须用逗号来分开。其顺序可以任意摆放。

l         l  With Encryption : With Encryption 是用来加密触发器的,放在 “On 数据表名或视图名 ” 的后面, “For” 的前面。如果使用了这句话,该触发器将会被加密,任何人都看不到触发器的内容了。

例一:以下是一个包含提醒电子邮件的触发器例子,如果订单表里记录有改动的的话(无论增加订单还是修改、删除订单),则给物流人员张三发送电子邮件:

CREATE TRIGGER 订单 _Insert

ON 订单

AFTER INSERT, UPDATE, DELETE

AS

   EXEC master..xp_sendmail ' 张三 ',

      ' 订单有更改,请查询确定 '

GO

例二:在订单明细表里,折扣字段不能大于 0.6 ,如果插入记录时,折扣大于 0.6 的话,回滚操作。

CREATE TRIGGER 订单明细 _Insert

   ON  订单明细

   AFTER INSERT

AS

BEGIN

         if (Select 折扣 from inserted)>0.6

         begin

                   print ' 折扣不能大于 0.6'

                   Rollback Transaction

         end

END

GO

在示例二中运用了两个方法,一个是前面说过的,在 Inserted 表里查询某个字段,还有一个是用 Rollback Transaction 来回滚操作。如果用下面的 SQL 语句来进行 Insert 操作的话,插入记录将会不成功。

INSERT INTO 订单明细 ( 订单 ID, 产品 ID, 单价 , 数量 , 折扣 )  

VALUES (11077,1,18,1,0.7)

运行结果如图 11.7 所示:

 

图 11.7 插入记录不符合触发器里的约束,则回滚操作

11.6  设置 After 触发器的激活顺序

对于同一个操作,如 Insert 、 Update 或 Delete 来说,可以建立多个 After Insert 触发器,在 11.5.1 节中,已经建立了一个名为 “ 产品 _Insert” 的触发器,现在再建立一个 After Insert 触发器,作用也是输出一句有好提示,提示内容为: “ 再一次告诉你,你又添加了一种产品 ” 。

CREATE TRIGGER 产品 _Insert1

   ON  产品

   AFTER INSERT

AS

BEGIN

         print ' 再一次告诉你,你又添加了一种产品 '

END

GO

重新运行一下插入产品的 SQL 语句:

INSERT INTO 产品 ( 产品名称 )

VALUES (' 大苹果 ')

如图 11.8 所示,运行一个 Insert 语句,在【消息】可以看到一共输出了两句话,说明激活两个不同的触发器。

 

图 11.8 一个语句激活两个触发器

当同一个操作定义的触发器越来越多的时候,触发器被激活的次序就会 变得越来越重要了。在 SQL Server 2005 里,用存储过程【 sp_settriggerorder 】可以为每一个操作各指定一个最先执行的 After 触发器和最后执行的 After 触发器。 sp_settriggerorder 语法如下:

sp_settriggerorder [ @triggername = ] '[ triggerschema. ] triggername'

        , [ @order = ] 'value'

        , [ @stmttype = ] 'statement_type'

        [ , [ @namespace = ] { 'DATABASE' | 'SERVER' | NULL } ]

翻译成中文就是

sp_settriggerorder 触发器名 ,

        激活次序 ,

        激活触发器的动作

解释如下:

l  触发器名,要用单引号括起来,因为它是一个字符串。

l  激活次序可以为 First 、 Last 和 None : First 是指第一个要激活的触发器; Last 是指它最后一个要激活的触发器; None 是不指激活序,由程序任意触发。

l  激活触发器的动作可以是: Insert 、 Update 和 Delete 。

上面的例子里,先激活的是【产品 _Insert 】触发器,后激活的是【产品 _Insert1 】触发器。如果把【产品 _Insert1 】触发器设为 First 触发器,把【产品 _Insert 】触发器设为 Last 触发器,那么结果将会完全不一样。设置语句如下:

Exec sp_settriggerorder

         ' 产品 _Insert1','First','Insert'

go

Exec sp_settriggerorder

         ' 产品 _Insert',’Last’,'Insert'

Go

重新运行一下插入产品的 SQL 语句:

INSERT INTO 产品 ( 产品名称 )

VALUES (' 大苹果 ')

运行结果如图 11.9 ,与图 11.8 比较一下,是不是激活次序已经发生变化了?

 

图 11.9 按次序激活的激活器

在设置 After 触发器激活顺序时,还有几点是需要注意的:

l  每个操作最多只能设一个 First 触发器和一个 Last 触发器。

l  如果要取消已经设好的 First 触发器或 Last 触发器,只要把它们设为 None 触发器即可。

l  如果用 Alter 命令修改过触发器内容后,该触发器会自动变成 None 触发器。所以用 Alter 命令也可以用来取消已经设好的 First 触发器或 Last 触发器。

l  只有 After 触发器可以设置激活次序, Instead Of 触发器不可以设置激活次序。

l  激活触发器的动作必须和触发器内部的激活动作一致。举例说明: After Insert 触发器,只能为 Insert 操作设置激活次序,不能为 Delete 操作设置激活次序。以下的设置是错误的:

Exec sp_settriggerorder

         ' 产品 _Insert1','First',’Update’

go

11.7  触发器的嵌套

当一个触发器执行时,能够触活另一个触发器,这种情况就是触发器的嵌套。在 SQL Server 2005 里,触发器能够嵌套到 32 层。

如果不想对触发器进行嵌套的话,可以通过【允许触发器激活其他触发器】的服务器配置选项来控制。但不管此设置是什么,都可以嵌套 Instead Of 触发器。设置触发器嵌套的选项更改方法为:

( 1 )打开 Management Studio ,在【对象资源管理】中,右击服务器名,并选择【属性】选项。

( 2 )单击【高级】节点。

( 3 )在【杂项】里设置【允许触发器激活其他触发器】为 True 或 False 。如图 11.10 所示:

 

图 11.10 开启 / 关闭触发器嵌套

现在,在 Northwind 数据库里建一个操作记录表,用来记录所有数据表的操作,无论是对哪个数据表进行了插入、更新或删除,都可以把操作内容和操作时间记录到操作记录表里。下面是建立操作记录表的 SQL 语句:

CREATE TABLE 操作记录表 (

         编号 int IDENTITY(1,1) NOT NULL,

         操作表名 varchar(50) NOT NULL,

         操作语句 varchar(2000) NOT NULL,

         操作内容 varchar(2000) NOT NULL,

         操作时间 datetime NOT NULL

         CONSTRAINT DF_ 操作记录表 _ 操作时间 DEFAULT (getdate()),

 CONSTRAINT PK_ 操作记录表 PRIMARY KEY CLUSTERED

(

         编号 ASC

)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]

) ON [PRIMARY]

GO

为了简便起见,在操作记录表里,只建一个 After Insert 触发器,触发器的作用是输入一条语句: “ 数据库又有记录变动了 ” 。在实际应用时,读者可自行修改成所需的代码。

CREATE TRIGGER 操作记录表 _Insert

   ON  操作记录表

   AFTER INSERT

AS

BEGIN

         print ' 数据库又有记录变动了 '

END

GO

作为示例,只在类别表里建立一个 After Insert 触发器,当在类别表里插入一条记录的时候,该触发器向操作记录表里插入一条记录,而在操作记录表里插入记录时,将会触发操作记录表里的【操作记录表 _Insert 】触发器。

CREATE TRIGGER 类别 _Insert

   ON  类别

   AFTER INSERT

AS

BEGIN

         Declare

         @ 类别名称 nvarchar(15),

     @ 说明 nvarchar(max)

         set @ 类别名称 = (Select 类别名称 from inserted)

         set @ 说明 = (Select 说明 from inserted)

         INSERT INTO 操作记录表 ( 操作表名 , 操作语句 , 操作内容 )

     VALUES (' 类别表 ',' 插入记录 ',' 类别名称: '+@ 类别名称 +', 说明: '+@ 说明 )

END

GO

现在运行一下对类别表的插入语句

INSERT INTO 类别 ( 类别名称 , 说明 )

VALUES (' 书籍 ',' 各类图书 ')

运行结果如图 11.11 所示:

 

图 11.11 触发器嵌套被激活

在【消息】对话框可以看到 “ 数据库又有记录变动了 ” ,这说明,触发器已经被嵌套激活了。如果把【允许触发器激活其他触发器】的选项设为 False ,再看看运行结果:

 

图 11.12 触发器嵌套没有被激活

如图 11.12 所示,现在没有 “ 数据库又有记录变动了 ” 的提示输出,说明嵌套的触发器没有被激活。

11.8  触发器的递归

触发器的递归是指,一个触发器从其内部又一次激活该触发器。例如一 个 Insert 触发器的内部还有一条对本数据表插入记录的 SQL 语句,那么这个插入语句就有可能再一次激活这个触发器本身。当然,这种递归的触发器内部还 会有判断语句,要一定的情况下才会执行那个 SQL 语句,否则的话,就会变成死循环了。

上面的例子说的是直接递归的触发器,还有一种是间接递归的触发器, 举例说明:当向 A 表插入一条记录时,激活了 A 表的 Insert 触发器, A 表的 Insert 触发器里有一个 SQL 语句是对 B 表进行 Insert 操作的,而在 B 表的 Insert 触发器里也有一句话是对 A 表进行 Insert 操作的。这样就是触发器的间接递归。

一般情况来说, SQL Server 服务器是不允许递归的,如果要打开触发器递归的功能,同样是将【允许触发器激活其他触发器】设为 True ,如图 11.10 所示。

 

11.9  设计 Instead Of 触发器

Instead Of 触发器与 After 触发器的工作流程是不一样的。 After 触发器是在 SQL Server 服务器接到执行 SQL 语句请求之后,先建立临时的 Inserted 表和 Deleted 表,然后实际更改数据,最后才激活触发器的。而 Instead Of 触发器看起来就简单多了,在 SQL Server 服务器接到执行 SQL 语句请求后,先建立临时的 Inserted 表和 Deleted 表,然后就触发了 Instead Of 触发器,至于那个 SQL 语句是插入数据、更新数据还是删除数据,就一概不管了,把执行权全权交给了 Instead Of 触发器,由它去完成之后的操作。

11.9.1   Instead Of 触发器的使用范围

Instead Of 触发器可以同时在数据表和视图中使用,通常在以下几种情况下,建议使用 Instead Of 触发器:

l         l  数据库里的数据禁止修改:例如电信部门的通话记录是不能修改的,一旦修改,则通话费用的计数将不正确。在这个时候,就可以用 Instead Of 触发器来跳过 Update 修改记录的 SQL 语句。

l         l  有可能要回滚修改的 SQL 语句:如 11.5.3 节中的例二,用 After 触发器并不是一个最好的方法,如果用 Instead Of 触发器,在判断折扣大于 0.6 时,就中止了更新操作,避免在修改数据之后再回滚操作,减少服务器负担。

l         l  在视图中使用触发器:因为 After 触发器不能在视图中使用,如果想在视图中使用触发器,就只能用 Instead Of 触发器。

l         l  用自己的方式去修改数据:如不满意 SQL 直接的修改数据的方式,可用 Instead Of 触发器来控制数据的修改方式和流程。

11.9.2   设计简单的 Instead Of 触发器

Instead Of 触发器的语法如下:

CREATE TRIGGER 触发器名

   ON  数据表名或视图名

   Instead Of INSERT 或 DELETE 或 UPDATE

AS

BEGIN

         -- 这里是要运行的 SQL 语句

END

GO

从上面可以看得出, Instead Of 触发器与 After 触发器的语法几乎一致,只是简单地把 After 改为 Instead Of 。前面说过的 11.5.3 节中的例二,用 After 触发器并不是一个最好的方法,如果用 Instead Of 触发器,在判断折扣大于 0.6 时,就中止了更新操作,避免在修改数据之后再回滚操作,减少服务器负担。现将原来的触发器改为 Instead Of 触发器:

CREATE TRIGGER 订单明细 _Insert

   ON  订单明细

   Instead Of INSERT

AS

BEGIN

         SET NOCOUNT ON;

         declare

         @ 订单 ID int,

         @ 产品 ID int,

         @ 单价 money,

         @ 数量 smallint,

         @ 折扣 real

         set @ 订单 ID = (select 订单 ID from inserted)

         set @ 产品 ID = (select 产品 ID from inserted)

         set @ 单价 = (select 单价 from inserted)

         set @ 数量 = (select 数量 from inserted)

         set @ 折扣 = (select 折扣 from inserted)

         if (@ 折扣 )>0.6

                            print ' 折扣不能大

11.10  查看 DML 触发器

查看已经设计好的 DML 触发器有两种方式,一种是通用 Management Studio 来查看,一种是利用系统存储过程来查看。

11.10.1   在 Management Studio 中查看触发器

在 Management Studio 中查看触发器的步骤:

( 1 )启动 Management Studio ,登录到指定的服务器上。

( 2 )在如图 11.13 所示界面的【对象资源管理器】下选择【数据库】,定位到要查看触发器的数据表上,并找到【触发器】项。

 

图 11.13 查看触发器列表

( 3 )单击【触发器】,在右边的【摘要】对话框里,可以看到已经建 好的该数据表的触发器列表。如果在点击【触发器】后,右边没有显示【摘要】对话框,可以在单击菜单栏上的【视图】菜单,选择【摘要】选项,打开【摘要】对 话框。如果在【摘要】对话框里没有看到本应存在的触发器列表,可以【摘要】对话框里右击空白处,在弹出的快捷菜单中选择【刷新】选项,刷新对话框后即可看 到触发器列表。

( 4 )双击要查看的触发器名, Management Studio 自动弹出一个【查询编辑器】对话框,对话框里显示的是该触发器的内容,如图 11.14 所示:

 

图 11.14 查看触发器内容

11.10.2   用系统存储过程查看触发器

SQL Server 2005 里已经建好了两个系统存储过程,可以用这两个系统存储过程来查看触发器的情况:

11.10.2 .1  sp_help :

系统存储过程 “sp_help” 可以了解如触发器名称、类型、创建时间等基本信息,其语法格式为:

sp_help  ‘ 触发器名 ’

举例:

sp_help  ' 产品 _Insert'

运行结果如图 11.15 所示,可以看到触发器 “ 产品 _insert” 的基本情况。

 

图 11.15 查看触发器的基本情况

11.10.2 .2  sp_helptext :

系统存储过程 “sp_helptext” 可以查看触发器的文本信息,其语法格式为:

sp_helptext  ‘ 触发器名 ’

举例:

sp_helptext  ' 产品 _Insert'

运行结果如图 11.16 所示,可以看到触发器 “ 产品 _insert” 的具体文本内容。

 

图 11.16 查看触发器的基本情况

于 0.6'

         else

                            INSERT INTO 订单明细

                                     ( 订单 ID, 产品 ID, 单价 , 数量 , 折扣 )

                            VALUES

                                     (@ 订单 ID,@ 产品 ID,@ 单价 ,@ 数量 ,@ 折扣 )

         END

GO

上面的触发器里写入了一句 “SET NOCOUNT ON” ,这一句的作用是,屏蔽在触发器里 Insert 语句执行完之后返回的所影响行数的消息。

 

11.11  修改 DML 触发器

在 Management Studio 中修改触发器之前,必须要先查看触发器的内容,通过 11.10.1 节的第( 1 )步到第( 4 )步,细心的读者可以已经发现,如图 11.14 所 示,在【查询编辑器】对话框里显示的就是用来修改触发器的代码。编辑完代码之后,单击【执行】按钮运行即可。修改触发器的语法如下:

ALTER  TRIGGER 触发器名

   ON  数据表名或视图名

   AFTER INSERT 或 DELETE 或 UPDATE

AS

BEGIN

         -- 这里是要运行的 SQL 语句

END

GO

如果只要修改触发器的名称的话,也可以使用存储过程 “sp_rename” 。其语法如下:

sp_rename ‘ 旧触发器名 ’,’ 新触发器名 ’

值得一提的是修改触发器名称有可能会使某些脚本或存储过程运行出错。

  

11.12  删除 DML 触发器

在 Management Studio 中删除触发器,必须要先查到触发器列表,通过 11.10.1 节的第( 1 )步到第( 3 )步,可以查看到数据表下的所有触发器列表,右击其中一个 触发器,在弹出快捷菜单中选择【删除】选项,此时将会弹出【删除对象】对话框,在该对话框中单击【确定】按钮,删除操作完成。用以下 SQL 语句也对可删除 触发器:

Drop Trigger 触发器名

注意:如果一个数据表被删除,那么 SQL Server 会自动将与该表相关的触发器删除。

  

11.13  禁用与启用 DML 触发器

禁用触发器与删除触发器不同,禁用触发器时,仍会为数据表定义该触发器,只是在执行 Insert 、 Update 或 Delete 语句时,除非重新启用触发器,否则不会执行触发器中的操作。

在 Management Studio 中禁用或启用触发器,也必须要先查到触发器列表,触发器列表里,右击其中一个触发器,在弹出快捷菜单中选择【禁用】选项,即可禁用该触发器。启用触发器与上类似,只是在弹出快捷菜单中选择【启用】选项即可。

用以下 Alter Table 语句也禁用或启用触发器,其语法如下:

Alter table 数据表名

  Disable 或 Enable trigger 触发器名或 ALL

用 Disable 可以禁用触发器,用 Enable 可以启用触发器;如果要禁用或启用所有触发器,用 “ALL” 来代替触发器名。

 

 11.14  2005 新增功能: DDL 触发器

DDL 触发器是 SQL Server 2005 新增的一个触发器类型,是一种特殊的触发器,它在响应数据定义语言( DDL )语句时触发。一般用于数据库中执行管理任务。

与 DML 触发器一样, DDL 触发器也是通过事件来激活,并执行其中 的 SQL 语句的。但与 DML 触发器不同, DML 触发器是响应 Insert 、 Update 或 Delete 语句而激活的, DDL 触发器是响应 Create 、 Alter 或 Drop 开头的语句而激活的。一般来说,在以下几种情况下可以使用 DDL 触发器:

l         l  数据库里的库架构或数据表架构很重要,不允许被修改。

l         l  防止数据库或数据表被误操作删除。

l         l  在修改某个数据表结构的同时修改另一个数据表的相应的结构。

l         l  要记录对数据库结构操作的事件。

 

 

11.15  2005 新增功能:设计 DDL 触发器

只要注意到 DDL 触发器和 DML 触发器的区别,设计 DDL 触发器与设计 DML 触发器也很类似,下面详细讲述一下要怎么去设计一个 DDL 触发器。

11.15.1   建立 DDL 触发器的语句

建立 DDL 触发器的语法代码如下:

CREATE TRIGGER trigger_name

ON { ALL SERVER | DATABASE }

[ WITH [ ,...n ] ]

{ FOR | AFTER } { event_type | event_group } [ ,...n ]

AS { sql_statement  [ ; ] [ ...n ] | EXTERNAL NAME < method specifier >  [ ; ] }

用中文取代一下英文可以看得更明白:

CREATE TRIGGER 触发器名
ON  ALL SERVER 或 DATABASE

FOR 或 AFTER

激活 DDL 触发器的事件

AS

         要执行的 SQL 语句

其中:

l  ON 后面的 All Server 是将 DDL 触发器作用到整个当前的服务器上。如果指定了这个参数,在当前服务器上的任何一个数据库都能激活该触发器。

l  ON 后面的 Database 是将 DDL 触发器作用到当前数据库,只能在这个数据库上激活该触发器。

l  For 或 After 是同一个意思,指定的是 After 触发器, DDL 触发器不能指定的 Stead Of 触发器。

l  激活 DDL 触发器的事件包括两种,在 DDL 触发器作用在当前数据库情况下可以使用以下事件:

CREATE_APPLICATION_ROLE
 ALTER_APPLICATION_ROLE
 DROP_APPLICATION_ROLE
 
CREATE_ASSEMBLY
 ALTER_ASSEMBLY
 DROP_ASSEMBLY
 
ALTER_AUTHORIZATION

_DATABASE
  
  
 
CREATE_CERTIFICATE
 ALTER_CERTIFICATE
 DROP_CERTIFICATE
 
CREATE_CONTRACT
 DROP_CONTRACT
  
 
GRANT_DATABASE
 DENY_DATABASE
 REVOKE_DATABASE
 
CREATE_EVENT_NOTIFICATION
 DROP_EVENT_NOTIFICATION
  
 
CREATE_FUNCTION
 ALTER_FUNCTION
 DROP_FUNCTION
 
CREATE_INDEX
 ALTER_INDEX
 DROP_INDEX
 
CREATE_MESSAGE_TYPE
 ALTER_MESSAGE_TYPE
 DROP_MESSAGE_TYPE
 
CREATE_PARTITION_FUNCTION
 ALTER_PARTITION_FUNCTION
 DROP_PARTITION_FUNCTION
 
CREATE_PARTITION_SCHEME
 ALTER_PARTITION_SCHEME
 DROP_PARTITION_SCHEME
 
CREATE_PROCEDURE
 ALTER_PROCEDURE
 DROP_PROCEDURE
 
CREATE_QUEUE
 ALTER_QUEUE
 DROP_QUEUE
 
CREATE_REMOTE_SERVICE

_BINDING
 ALTER_REMOTE_SERVICE

_BINDING
 DROP_REMOTE_SERVICE

_BINDING
 
CREATE_ROLE
 ALTER_ROLE
 DROP_ROLE
 
CREATE_ROUTE
 ALTER_ROUTE
 DROP_ROUTE
 
CREATE_SCHEMA
 ALTER_SCHEMA
 DROP_SCHEMA
 
CREATE_SERVICE
 ALTER_SERVICE
 DROP_SERVICE
 
CREATE_STATISTICS
 DROP_STATISTICS
 UPDATE_STATISTICS
 
CREATE_SYNONYM
 DROP_SYNONYM
 CREATE_TABLE
 
ALTER_TABLE
 DROP_TABLE
  
 
CREATE_TRIGGER
 ALTER_TRIGGER
 DROP_TRIGGER
 
CREATE_TYPE
 DROP_TYPE
  
 
CREATE_USER
 ALTER_USER
 DROP_USER
 
CREATE_VIEW
 ALTER_VIEW
 DROP_VIEW
 
CREATE_XML_SCHEMA

_COLLECTION
 ALTER_XML_SCHEMA

_COLLECTION
 DROP_XML_SCHEMA

_COLLECTION
 

在 DDL 触发器作用在当前服务器情况下,可以使用以下事件:

ALTER_AUTHORIZATION_SERVER
  
  
 
CREATE_DATABASE
 ALTER_DATABASE
 DROP_DATABASE
 
CREATE_ENDPOINT
 DROP_ENDPOINT
  
 
CREATE_LOGIN
 ALTER_LOGIN
 DROP_LOGIN
 
GRANT_SERVER
 DENY_SERVER
 REVOKE_SERVER
 

例三,建立一个 DDL 触发器,用于保护数据库中的数据表不被修改,不被删除。具体操作步骤如下:

( 1 )启动 Management Studio ,登录到指定的服务器上。

( 2 )在如图 11.1 所示界面的【对象资源管理器】下选择【数据库】,定位到【 Northwind 】数据库上。

( 3 )单击【新建查询】按钮,在弹出的【查询编辑器】的编辑区里输入以下代码:

CREATE TRIGGER 禁止对数据表操作

ON DATABASE

FOR DROP_TABLE, ALTER_TABLE

AS

   PRINT ' 对不起,您不能对数据表进行操作 '

   ROLLBACK ;

( 4 )单击【执行】按钮,生成触发器。

例四,建立一个 DDL 触发器,用于保护当前 SQL Server 服务器里所有数据库不能被删除。具体代码如下:

CREATE TRIGGER 不允许删除数据库

ON all server 

FOR DROP_DATABASE

AS

   PRINT ' 对不起,您不能删除数据库 '

   ROLLBACK ;

GO

例五,建立一个 DDL 触发器,用来记录数据库修改状态。具体操作步骤如下:

( 1 )建立一个用于记录数据库修改状态的表:

CREATE TABLE 日志记录表 (

         编号 int IDENTITY(1,1) NOT NULL,

         事件 varchar(5000) NULL,

         所用语句 varchar(5000) NULL,

         操作者 varchar(50) NULL,

         发生时间 datetime NULL,

 CONSTRAINT PK_ 日志记录表 PRIMARY KEY CLUSTERED

(

         编号 ASC

)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]

) ON [PRIMARY]

GO

( 2 )建立 DDL 触发器:

CREATE TRIGGER 记录日志

ON DATABASE

FOR DDL_DATABASE_LEVEL_EVENTS

AS

DECLARE @log XML

SET @log = EVENTDATA()

INSERT  日志记录表

   ( 事件 , 所用语句 , 操作者 , 发生时间 )

   VALUES

   (

   @log.value('(/EVENT_INSTANCE/EventType)[1]', 'nvarchar(100)'),

   @log.value('(/EVENT_INSTANCE/TSQLCommand)[1]', 'nvarchar(2000)'),

   CONVERT(nvarchar(100), CURRENT_USER),

   GETDATE()

   ) ;

GO

其中 Eventdata 是个数据库函数,它的作用是以 XML 格式返回有关服务器或数据库事件的信息。 @log.value 是返回 log 这个 XML 结点的值,结点的位置是括号里的第一个参数。

11.15.2   测试触发器功能

现在测试一下在上一章节中建立好的三个触发器的功能。下面所有的测试都是在【查询编辑器】对话框里进行的,要打开【查询编辑器】对话框,只要单击 Management Studio 里【新建查询】按钮即可。

测试例三:例三是保证【 Northwind 】数据库里不能删除表和修改表,在【查询编辑器】对话框里输入一个删除表的 SQL 语句:

Drop table 操作记录表

运行结果如图 11.17 所示:

 

图 11.17 不允许删除表格

测试例四:例四是保证当前服务器里的所有数据库不能被删除,在【查询编辑器】对话框里输入一个删除数据库的 SQL 语句:

Drop DataBase test

运行结果如图 11.18 所示:

 

图 11.18 不允许删除数据库

测试例五:例五是记录对【 Northwind 】所进行的操作,在【查询编辑器】对话框里输入一条添加数据表和一条删除数据表的 SQL 语句,然后再用 Select 语句查看【目志记录表】数据表里所有的记录:

CREATE TABLE 测试表 (

         编号 int IDENTITY(1,1) NOT NULL,

         测试内容 varchar(50) NOT NULL)

GO

Drop table 测试表

GO

select * from 日志记录表

GO

运行时不要忘了,前面曾经建立过一个不能删除数据表的触发器,要先把它禁用或删除。运行结果如图 11.19 所示:

 

图 11.19 记录对数据库的操作

 

 

11.16  2005 新增功能:查看与修改 DDL 触发器

DDL 触发器有两种,一种是作用在当前 SQL Server 服务器上的,一种是作用在当前数据库中的。这两种 DDL 触发器在 Management Studio 中所在的位置是不同的。

l  作用在当前 SQL Server 服务器上的 DDL 触发器所在位置是:【对象资源管理器】,选择所在 SQL Server 服务器,定位到【服务器对象】 à 【触发器】,在【摘要】对话框里就可以看到所有的作用在当前 SQL Server 服务器上的 DDL 触发器。

l  作用在当前数据库中的 DDL 触发器所在位置是:【对象资源管理器】,选择所在 SQL Server 服务器,【数据库】,所在数据库,定位到【可编程性】 à 【数据库触发器】,在摘要对话框里就可以看到所有的当前数据库中的 DDL 触发器。

右击触发器,在弹出的快捷菜单中选择【编写数据库触发器脚本为】 à 【 CREATE 到】 à 【新查询编辑器对话框】,然后在新打开的【查询编辑器】对话框里可以看到该触发器的内容。

在 Management Studio 如果要修改 DDL 触发器内容,就只能先删除该触发器,再重新建立一个 DDL 触发器。

虽然在 Management Studio 中没有直接提供修改 DDL 触发器的对话框,但在【查询编辑器】对话框里依然可以用 SQL 语句来进行修改。下面给出几个对 DDL 触发器操作常用 的 SQL 代码,由于对 DDL 触发器的操作和对 DML 触发器的操作类似,因此不再详细说明用法。

l         l  创建 DDL 触发器

CREATE TRIGGER (Transact-SQL)

l         l  删除 DDL 触发器

DROP TRIGGER (Transact-SQL)

l         l  修改 DDL 触发器

ALTER TRIGGER (Transact-SQL)

l         l  重命名 DDL 触发器

sp_rename (Transact-SQL)

l         l  禁用 DDL 触发器

DISABLE TRIGGER (Transact-SQL)

l         l  启用 DDL 触发器

ENABLE TRIGGER (Transact-SQL)

l         l  删除 DDL 触发器

DROP TRIGGER (Transact-SQL)

 

 

11.17  触发器的应用技巧

触发器的使用范围很广,使用的频率也很高,触发器的应用技巧也层出不穷,下面介绍一些在触发器里常用的技巧,希望可以做到抛砖引玉之功效。

11.17.1   如何知道触发器修改了多少条记录

需要注意的是,一种操作类型( Insert 、 Update 或 Delete )虽然可以激活多 个触发器,但是每个操作类型在一次操作时,对一个触发器只激活一次。例如,运行一个 Update 语句,有可能一次更新了十条记录,但是对于 After Update 这个触发器,只激活一次,而不是十次。但是在 Inserted 表和 Deleted 表里会有十条记录,这个时候,只要利用 @@Rowcount 这个系统变量就可以得知更新了多少条记录。例如:

CREATE TRIGGER 订单明细删除 _test

   ON  订单明细

   AFTER DELETE

AS

BEGIN

         print ' 您此次删除了 ' + Cast(@@rowcount as varchar) + ' 条记录 '

END

GO

Delete FROM 订单明细 where 折扣 =0.25

GO

Delete FROM 订单明细 where 订单 ID='123456789'

GO

这里先是建立了一个名为 “ 订单明细删除 _test” 的触发器,作用就是显示删除了多少条记录。之后执行两个 SQL 语句,一个是删除折扣为 0.25 的记录,一个是删除订单 ID 号为 123456789 的记录,这条记录是不存在的。运行结果如图 11.20 所示:

 

图 11.20 显示删除的记录数

在图 11.20 可以看出,用系统变量 @@rowcount 可以获得删除记录的条数。另外,在图中还可以看出,虽然第二个 SQL 语句删除的记录数为零,但是触发器还是被激活了。因此可以知道,触发器只与激活它的类型有关,与具体操作的记录数无关。

11.17.2   如何知道插入记录的自动编号是多少

在第 11.7 节,触发器的嵌套里,【类别】数据表设计了一个触发 器,当在【类别】数据表里插入一件记录的时候,将会在【操作记录表】里也插入一条记录,用来记录具体的插入操作的,其实这个触发器还可以写得更好,不但可 以记录插入操作所用的 SQL 语句,还可以记录下当时插入记录时候,数据库为这个记录自动生成编号是多少,为以后的操作提供更大的便利。修改该触发器的代码 如下:

ALTER TRIGGER 类别 _Insert

   ON  类别

   AFTER INSERT

AS

BEGIN

         Declare

         @ 类别名称 nvarchar(15),

     @ 说明 nvarchar(max)

         set @ 类别名称 = (Select 类别名称 from inserted)

         set @ 说明 = (Select 说明 from inserted)

         INSERT INTO 操作记录表 ( 操作表名 , 操作语句 , 操作内容 )

     VALUES (' 类别表 ',' 插入记录 ',

                   ' 插入了 ID 号为 '+cast(@@IDENTITY as varchar)+' 的记录:类别名称: '

                            +@ 类别名称 +', 说明: '+@ 说明 )

END

GO

从上面的代码可以看出,用 @@IDENTITY 可以获得刚插入记录的标识值,在本例中是它的主键值。插入记录后,在【操作记录表】里可以详细查看到插入的记录的编号以及它的内容。

11.17.3   如何知道某个字段是否被修改

在 Update 触发器和 Insert 触发器里,可以用 “Update( 字段名 )” 来判断某个字段是不是被更改,返回的是一个布尔值。例如定单生成后,只能修改折扣的触发器:

CREATE TRIGGER 只允许修改折扣

   ON   订单明细

   Instead Of UPDATE

AS

BEGIN

         SET NOCOUNT ON;

         if update( 折扣 )

                   begin

                            declare

                            @ 订单 ID int,

                            @ 产品 ID int,

                            @ 折扣 real

                            set @ 订单 ID = (select 订单 ID from inserted)

                            set @ 产品 ID = (select 产品 ID from inserted)

                            set @ 折扣 = (select 折扣 from inserted)

                            update 订单明细 set 折扣 =@ 折扣

                                     where 订单 ID=@ 订单 ID and 产品 ID=@ 产品 ID

                   end

         else

         begin

                   print ' 只能更改折扣字段 '

         end

END

GO

update 订单明细 set 折扣 =0.2

         where 订单 ID=10288 and 产品 ID=54

Go

update 订单明细 set 订单 ID=10288

         where 订单 ID=10288 and 产品 ID=54

Go

上面的代码,先建立了一个触发器,只有修改了折扣字段的 Update 语句才会被执行。然后写了两个 Update 的 SQL 语句,一个是修改了折扣字段的,一个是没有修改折扣字段的。运行后的结果如图 11.21 所示。第一个 SQL 语句被正确执行,第二个 SQL 语句没有被执行。

 

图 11.21 用 Update 判断字段是否被修改

11.17.4   如何返回错误信息

虽然上面介绍触发器时,用过很多次 Print 来输出自定义的信息,但是实际上,只有在用【查询编辑器】中运行 SQL 语句才能看得到这些自定义的信息,而其他的前端应用程序都不会显示出这些自定义的信息,包括用 Management Studio 也一样。

读者可以自行测试一下,在 Management Studio 里打开【订单明细】数据表,因为上面建了一个【只允许修改折扣】的触发器,所以只要在不是折扣的字段里修改数据后,再将鼠标聚焦到其他记录上 时,被修改的数据马上就会回滚到修改前的状态,在这个过程中,几乎是看不到什么提示的。如果想要在这个过程中看到提示的话,就要将触发器修改一下,加上 “Raiserror” 语句,具体修改代码如下:

set ANSI_NULLS ON

set QUOTED_IDENTIFIER ON

go

ALTER TRIGGER 只允许修改折扣
   ON  订单明细

   Instead Of UPDATE

AS

BEGIN

         SET NOCOUNT ON;

         if update( 折扣 )

                   begin

                            declare

                            @ 订单 ID int,

                            @ 产品 ID int,

                            @ 折扣 real

                            set @ 订单 ID = (select 订单 ID from inserted)

                            set @ 产品 ID = (select 产品 ID from inserted)

                            set @ 折扣 = (select 折扣 from inserted)

                            update 订单明细 set 折扣 =@ 折扣

                                     where 订单 ID=@ 订单 ID and 产品 ID=@ 产品 ID

                   end

         else

                   begin

                            print ' 只能更改折扣字段 '

                            Raiserror(' 除了折扣字段之外的其他字段信息不能修改 ',16,5)

                   end

END

修改完触发器之后,再去修改其他非 “ 折扣 ” 字段的内容时,就会弹出错误提示,如图 11.22 所示, Raiserror 的用法可以查看 SQL Server 2005 的帮助。

 

图 11.22 显示错误信息

 

 

11.18  小结

触发器是与数据库和数据表相结合的特殊的存储过程,当数据表有 Insert 、 Update 、 Delete 操作或数据库有 Create 、 Alter 、 Drop 操作的时候,可以激活触发器,并运行其中的 T-SQL 语句。

在 SQL Server 2005 中触发器分为 DML 触发器和 DDL 触发器两种。其中 DML 触发器又分为 After 触发器和 Instead Of 触发器两种。 After 触发器是先修改记录后激活的触发器; Instead Of 触发器是 “ 取代 ” 触发器。 DDL 触发器根据作用范围可以分为作用在数据库的触发器和作用在服务器的触发器两种。 After 触发器只能用于数据表中,而 Instead Of 触发器即可以用在数据表中,也可以用在视图中。

使用 CREATE TRIGGER 语句可以创建触发器,使用 ALTER TRIGGER 语句可以修改触发器,使用 Drop Trigger 语句可以删除触发器。触发器允许嵌套和递归,嵌套最多可以是 32 层。

 

如何实现 SQLServer 行级触发器
ⅰ 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 游标,但是在触发器内一般的做法是不建议使用游标。因此这里只记录下这个思路,如果您真的要实现它,那么我想不是多么复杂。但是我想,一个程序员一旦应有了这种自主性,那么它往往就不会真的就只在这个触发器内模拟一个行级触发的功能吧。或许有一天我会真的需要它,那时候再来补上这段代码吧


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/chinayuan/archive/2011/03/31/6292335.aspx