触发器是一种特殊类型的存储过程,当对数据库进行UPDATE,DELETE或INSERT时,触发器会生效.触发器可以查询其它表,而且可以包含复杂的 SQL 语句。
创建触发器语法:
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 ]
}
}
使用触发器的优点
• 触发器是自动的:它们在对表的数据作了任何修改(比如手工输入或者应用程序采取的操作)之后立即被激活。
• 触发器可以通过数据库中的相关表进行层叠更改。例如,可以在 titles 表的 title_id 列上写入一个删除触发器,以使其它表中的各匹配行采取删除操作。该触发器用 title_id 列作为唯一键,在 titleauthor、sales 及 roysched 表中对各匹配行进行定位。
• 触发器可以强制限制,这些限制比用 CHECK 约束所定义的更复杂。与 CHECK 约束不同的是,触发器可以引用其它表中的列。例如,触发器可以回滚试图对价格低于 10 美元的书(存储在 titles 表中)应用折扣(存储在 discounts 表中)的更新。
定义一个无论何时用INSERT语句向表中插入数据时都会执行的触发器。
当触发INSERT触发器时,新的数据行就会被插入到触发器表和inserted表中。inserted表是一个逻辑表,它包含了已经插入的数据行的一个副本。inserted表包含了INSERT语句中已记录的插入动作。inserted表还允许引用由初始化INSERT语句而产生的日志数据。触发器通过检查inserted表来确定是否执行触发器动作或如何执行它。inserted表中的行总是触发器表中一行或多行的副本。
日志记录了所有修改数据的动作(INSERT、UPDATE和DELETE语句),但在事务日志中的信息是不可读的。然而,inserted表允许你引用由INSERT语句引起的日志变化,这样就可以将插入数据与发生的变化进行比较,来验证它们或采取进一步的动作。也可以直接引用插入的数据,而不必将它们存储到变量中。
示例
在本例中,将创建一个触发器。无论何时订购产品(无论何时向Order Details表中插入一条记录),这个触发器都将更新Products表中的一列(UnitsInStock)。用原来的值减去订购的数量值即为新值。
USE Northwind
CREATE TRIGGER OrdDet_Insert
ON [Order Details]
FOR INSERT
AS
UPDATE P SET
UnitsInStock = P.UnitsInStock – I.Quantity
FROM Products AS P INNER JOIN Inserted AS I
ON P.ProductID = I.ProductID
DELETE触发器的工作过程
当触发DELETE触发器后,从受影响的表中删除的行将被放置到一个特殊的deleted表中。deleted表是一个逻辑表,它保留已被删除数据行的一个副本。deleted表还允许引用由初始化DELETE语句产生的日志数据。
使用DELETE触发器时,需要考虑以下的事项和原则:
•当某行被添加到deleted表中时,它就不再存在于数据库表中;因此,deleted表和数据库表没有相同的行。
•创建deleted表时,空间是从内存中分配的。deleted表总是被存储在高速缓存中。
•为DELETE动作定义的触发器并不执行TRUNCATE TABLE语句,原因在于日志不记录TRUNCATE TABLE语句。
示例
在本例中,将创建一个触发器,无论何时删除一个产品类别(即从Categories表中删除一条记录),该触发器都会更新Products表中的Discontinued列。所有受影响的产品都标记为1,标示不再使用这些产品了。
USE Northwind
CREATE TRIGGER Category_Delete
ON Categories
FOR DELETE
AS
UPDATE P SET Discontinued = 1
FROM Products AS P INNER JOIN deleted AS d
ON P.CategoryID = d.CategoryID
UPDATE触发器的工作过程
可将UPDATE语句看成两步操作:即捕获数据前像(before image)的DELETE语句,和捕获数据后像(after image)的INSERT语句。当在定义有触发器的表上执行UPDATE语句时,原始行(前像)被移入到deleted表,更新行(后像)被移入到inserted表。
触发器检查deleted表和inserted表以及被更新的表,来确定是否更新了多行以及如何执行触发器动作。
可以使用IF UPDATE语句定义一个监视指定列的数据更新的触发器。这样,就可以让触发器容易的隔离出特定列的活动。当它检测到指定列已经更新时,触发器就会进一步执行适当的动作,例如发出错误信息指出该列不能更新,或者根据新的更新的列值执行一系列的动作语句。
语法
例1
本例阻止用户修改Employees表中的EmployeeID列。
USE Northwind
GO
CREATE TRIGGER Employee_Update
ON Employees
FOR UPDATE
AS
IF UPDATE (EmployeeID)
BEGIN
RAISERROR ('Transaction cannot be processed.\
***** Employee ID number cannot be modified.', 10, 1)
ROLLBACK TRANSACTION
END
INSTEAD OF触发器的工作过程
可以在表或视图上指定INSTEAD OF触发器。执行这种触发器就能够替代原始的触发动作。INSTEAD OF触发器扩展了视图更新的类型。对于每一种触发动作(INSERT、UPDATE或 DELETE),每一个表或视图只能有一个INSTEAD OF触发器。
INSTEAD OF触发器被用于更新那些没有办法通过正常方式更新的视图。例如,通常不能在一个基于连接的视图上进行DELETE操作。然而,可以编写一个INSTEAD OF DELETE触发器来实现删除。上述触发器可以访问那些如果视图是一个真正的表时已经被删除的数据行。将被删除的行存储在一个名为deleted的工作表中,就像AFTER触发器一样。相似地,在UPDATE INSTEAD OF触发器或者INSERT INSTEAD OF触发器中,你可以访问inserted表中的新行。
不能在带有WITH CHECK OPTION定义的视图中创建INSTEAD OF触发器。
CHECK 约束
CHECK 约束指定可由表中一列或多列接受的数据值或格式。例如,可以要求 authors 表的 zip 列只允许输入五位数的数字项。
可以为一个表定义许多 CHECK 约束。可以使用"表"属性页创建、修改或删除每个 CHECK 约束。
约束
约束使您得以定义 Microsoft® SQL Server™ 2000 自动强制数据库完整性的方式。约束定义关于列中允许值的规则,是强制完整性的标准机制。使用约束优先于使用触发器、规则和默认值。查询优化器也使用约束定义生成高性能的查询执行计划。
约束类
SQL Server 2000 支持五类约束。
• NOT NULL 指定不接受 NULL 值的列。
• CHECK 约束对可以放入列中的值进行限制,以强制执行域的完整性。
CHECK 约束指定应用于列中输入的所有值的布尔(取值为 TRUE 或 FALSE)搜索条件,拒绝所有不取值为 TRUE 的值。可以为每列指定多个 CHECK 约束。下例显示名为 chk_id 约束的创建,该约束确保只对此关键字输入指定范围内的数字,以进一步强制执行主键的域。
CREATE TABLE cust_sample
(
cust_id int PRIMARY KEY,
cust_name char(50),
cust_address char(50),
cust_credit_limit money,
CONSTRAINT chk_id CHECK (cust_id BETWEEN 0 and 10000 )
)
• UNIQUE 约束在列集内强制执行值的唯一性。
对于 UNIQUE 约束中的列,表中不允许有两行包含相同的非空值。主键也强制执行唯一性,但主键不允许空值。UNIQUE 约束优先于唯一索引。
• PRIMARY KEY 约束标识列或列集,这些列或列集的值唯一标识表中的行。
在一个表中,不能有两行包含相同的主键值。不能在主键内的任何列中输入 NULL 值。在数据库中 NULL 是特殊值,代表不同于空白和 0 值的未知值。建议使用一个小的整数列作为主键。每个表都应有一个主键。
一个表中可以有一个以上的列组合,这些组合能唯一标识表中的行,每个组合就是一个候选键。数据库管理员从候选键中选择一个作为主键。例如,在 part_sample 表中,part_nmbr 和 part_name 都可以是候选键,但是只将 part_nmbr 选作主键。
CREATE TABLE part_sample
(part_nmbr int PRIMARY KEY,
part_name char(30),
part_weight decimal(6,2),
part_color char(15) )
• FOREIGN KEY 约束标识表之间的关系。
一个表的外键指向另一个表的候选键。当外键值没有候选键时,外键可防止操作保留带外键值的行。在下例中,order_part 表建立一个外键引用前面定义的 part_sample 表。通常情况下,order_part 在 order 表上也有一个外键,下面只不过是一个简单示例。
CREATE TABLE order_part
(order_nmbr int,
part_nmbr int
FOREIGN KEY REFERENCES part_sample(part_nmbr)
ON DELETE NO ACTION,
qty_ordered int)
GO
如果一个外键值没有候选键,则不能插入带该值(NULL 除外)的行。如果尝试删除现有外键指向的行,ON DELETE 子句将控制所采取的操作。ON DELETE 子句有两个选项:
• NO ACTION 指定删除因错误而失败。
• CASCADE 指定还将删除包含指向已删除行的外键的所有行。
如果尝试更新现有外键指向的候选键值,ON UPDATE 子句将定义所采取的操作。它也支持 NO ACTION 和 CASCADE 选项。
列约束和表约束
约束可以是列约束或表约束:
• 列约束被指定为列定义的一部分,并且仅适用于那个列(前面的示例中的约束就是列约束)。
• 表约束的声明与列的定义无关,可以适用于表中一个以上的列。
当一个约束中必须包含一个以上的列时,必须使用表约束。
例如,如果一个表的主键内有两个或两个以上的列,则必须使用表约束将这两列加入主键内。假设有一个表记录工厂内的一台计算机上所发生的事件。假定有几类事件可以同时发生,但不能有两个同时发生的事件属于同一类型。这一点可以通过将 type 列和 time 列加入双列主键内来强制执行。
CREATE TABLE factory_process
(event_type int,
event_time datetime,
event_site char(50),
event_desc char(1024),
CONSTRAINT event_key PRIMARY KEY (event_type, event_time) )
--触发器根据来源稿件的自定义属性(domainname)更新到推荐对象
--使前缀加上网站全路经
--推荐稿件对象表userobject6
--需要配合自定义函数得到2级自定义属性domainname名
CREATE TRIGGER [insert_suggest] ON [userobject6]
FOR INSERT
AS
DECLARE @link VARCHAR(256),
@id INT,
@domainname1 VARCHAR(1000),
@domainname VARCHAR(1000),
@domainname2 VARCHAR(1000),
@userprop2 VARCHAR(1000)
IF UPDATE(userprop3)
BEGIN
SELECT @link = userprop3,@domainname1 = userprop1,@userprop2 = userprop2,@id = idleaf FROM inserted
IF @@ROWCOUNT > 0
BEGIN
SELECT @domainname = dbo.uf_Getn1(@domainname1) FROM userobject6
IF @@ROWCOUNT > 0 and @domainname is not null and @domainname<>''
BEGIN
IF RIGHT(@domainname,1)='/'
set @domainname2 = SUBSTRING(@domainname,1,(LEN(@domainname)-1))
ELSE
set @domainname2 = @domainname
UPDATE userobject6 SET userprop2=@domainname2+'/'+@userprop2 WHERE idleaf = @id
END
END
END
--此自定义函数是根据栏目路径获取二级自定义属(domainname)名
--如输入值:‘/卫生局/头条新闻’
CREATE FUNCTION uf_Getn1 (@p_pathname VARCHAR(1000))
RETURNS VARCHAR(500)
AS
BEGIN
-- 如果出现错误,则返回/或是NULL
-- SQL 写法
DECLARE @v_NodeName varchar(200),
@v_Node varchar(1000),
@v_nodepath varchar(1000),
@first INT,
@second INT
set @v_NodeName = ''
set @v_Node = substring(@p_pathname,2,500)
IF @v_Node = ''
BEGIN
set @v_NodeName = 'null'
RETURN (@v_NodeName)
END
SELECT @first = CHARINDEX('/', @v_Node)
SELECT @second = CHARINDEX('/', @v_Node, @first + 1)
IF @second>0
SELECT @v_nodepath = SUBSTRING(@v_Node, 1, @second)
ELSE
SELECT @v_nodepath = @v_Node+'/'
BEGIN
set @v_NodeName = (SELECT userprop1
FROM NodeTable
where dbo.uf_getpath(idnode) = @v_nodepath)
if @@rowcount = 0
RETURN 'null'
END
RETURN @v_NodeName
END
触发器在增量同步数据的运用. (摘)
触发器可以记录对表的添加,修改,删除,
这样可以通过触发器,记录一段时间内的表的变动的记录,把这些记录存到一个变动记录表里,
数据同步的时候,就可以读取这张变成的记录表,只需要同步变动过的记录,这样可以大大提高同步的速度.
对原表删除的操作,可以能新表做DELETE操作
对原表进行Insert 或Update操作,可以执行新建或更新操作.
下面是一个触发器(SQL SERVER 2000)的例子:
if exists (select * from dbo.sysobjects where id = object_id(N'[risk].[TRG_GUARANTEE_R_IN_HG]') and OBJECTPROPERTY(id, N'IsTrigger') = 1)2
drop trigger [risk].[TRG_GUARANTEE_R_IN_HG]3
if exists (select * from dbo.sysobjects where id = object_id(N'[risk].[TRG_GUARANTEE_R_DEL_HG]') and OBJECTPROPERTY(id, N'IsTrigger') = 1)4
drop trigger [risk].[TRG_GUARANTEE_R_DEL_HG]5
CREATE TRIGGER TRG_GUARANTEE_R_IN_HG6
ON GUARANTEE_R7
FOR INSERT,UPDATE8
AS9
UPDATE A SET ARJ_MARK='M',CREATE_TIME=GETDATE(),USING_FLAG='0000000000'10
FROM LOG_ENTRY_INC2 A INNER JOIN INSERTED B ON A.ENTRY_ID = B.ENTRY_ID11
INSERT INTO LOG_ENTRY_INC2(ENTRY_ID,ARJ_MARK,CREATE_TIME,USING_FLAG)12
SELECT ENTRY_ID,'M',GETDATE(),'0000000000'13
FROM INSERTED where ENTRY_ID NOT IN (SELECT ENTRY_ID FROM LOG_ENTRY_INC2)14
CREATE TRIGGER TRG_GUARANTEE_R_DEL_HG15
ON GUARANTEE_R16
FOR DELETE17
AS18
UPDATE A SET ARJ_MARK='D',CREATE_TIME=GETDATE(),USING_FLAG='0000000000'19
FROM LOG_ENTRY_INC2 A INNER JOIN INSERTED B ON A.ENTRY_ID = B.ENTRY_ID20
INSERT INTO LOG_ENTRY_INC2(ENTRY_ID,ARJ_MARK,CREATE_TIME,USING_FLAG)21
SELECT ENTRY_ID,'D',GETDATE(),'0000000000'22
FROM DELETED where ENTRY_ID NOT IN (SELECT ENTRY_ID FROM LOG_ENTRY_INC2)触发器设计技巧与实例
关键字: 触发器 实例
在数据库设计中,有两种方法可设定自动化的资料处理规则,一种是条件约束,一种是触发器,一般而言,条件约束比触发器较容易设定及维护,且执行效率较好,但条件约束只能对资料进行简单的栏位检核,当涉及到多表操作等复杂操
作时,就要用到触发器了.
一个数据库系统中有两个虚拟表用于存储在表中记录改动的信息,分别是:虚拟表Inserted、虚拟表Deleted
在表记录新增时 存放新增的记录 不存储记录
修改时 存放用来更新的新记录 存放更新前的记录
删除时 不存储记录 存放被删除的记录
触发器的种类及触发时机
After触发器:触发时机在资料已变动完成后,它将对变动资料进行必要的善后与处理,若发现有错误,则用事务回滚(Rollback Transaction)将此次操作所更动的资料全部回复.
Istead of 触发器:触发时机在资料变动前发生,且资料如何变动取决于触发器.
现在介绍一下创建触发器的编写格式:
After类型:
Create Trigger 触发器名称
on 表名
after 操作(insert,update)
as
Sql语句
Instead of类型:
Create Trigger 触发器名称
on 表名
Instead of 操作(update,delete)
as
Sql语句
实例1:
在订单(表orders)中的订购数量(列名为num)有变动时,触发器会先到客户(表Customer)中取得该用户的信用等级(列名为Level),然后再到信用额度(Creit)中取出该等级许可的订购数量上下限,最后比较订单中的订购数量是否符合限制。
代码:
Create Trigger num_check
on orders
after insert,update
as
if update(num)
begin
if exists(select a.* from orders a join customer b on a.customerid=b.customerid
join creit c on b.level=c.level
where a.num between c.up and c.down)
begin
rollback transaction
exec master..xp_sendmail 'administrator','客户的订购数量不符合限制'
end
end
实例2:
有工资管理系统中,当公司对某员工甲的月薪进行调整时,通常会先在表员工中修改薪资列,然后在表员工记录中修改薪资调整时间与薪资
Create trigger compensation
on 员工
after update
as
if @@rowcount=0 return
if update(薪资)
begin
insert 员工记录
select 员工遍号,薪资,getdate()
from inserted
end
浙公网安备 33010602011771号