2008年11月9日所有随笔

接触触发器

触发器是数据库服务器中发生事件时自动执行的特种存储过程。如果用户要通过数据操作语言 (DML) 事件编辑数据,则执行 DML 触发器。DML 事件是针对表或视图的 INSERT、UPDATE 或 DELETE 语句。

现在有一个这样的需求,想在插入新数据到 news 表时,先对 title 字段进行检查,要是有相同数据的就停止插入。

先创建一个触发器:checknews

CREATE TRIGGER checknews -- checknews 为触发器名称
ON news -- ON 后面是触发器所属表
AFTER INSERT -- 触发器类型
AS
begin
if exists(select 1 from inserted join news on inserted.title=news.title)
  ROLLBACK TRANSACTION
end

咋一看,没什么问题,其实达不到我们想要的效果;指定触发器类型有三个关键词,分别是:FOR, AFTER, INSTEAD OF

AFTER:它是先执行操作,再运行触发器的,像上面的触发器,是永远都无法往表添加记录的。

FOR:如果仅指定 FOR 关键字,则 AFTER 为默认值,经测试我觉得 AFTER 和 FOR 为同样的效果。

INSTEAD OF:它不执行其所定义的操作(INSERT,UPDATE,DELETE),也就是用户操作,而执行触发器本身!也就是说,尽管触发器被触发,但相应的操作并不执行,而运行的仅是触发器 SQL 语句本身。对于统一操作只能定义一个 INSTEAD OF 触发器!

明白以上的,改一下触发器:

CREATE TRIGGER checknews
ON news
AFTER INSERT
AS
begin
declare @count int
select @count = count(*) from inserted join news on inserted.title=news.title
if @count > 1
  ROLLBACK TRANSACTION -- 回滚
end

至此,需求就实现了,如果说一定要用 exists,怎么实现呢?那就用 INSTEAD OF 了。


CREATE TRIGGER checknews
ON news
INSTEAD OF INSERT
AS
begin
if not exists(select 1 from inserted join news on inserted.title=news.title)
  INSERT INTO news SELECT [title], [image] FROM inserted
end


刚接触触发器(我就是)的朋友可能会问 inserted 是什么?它是一个临时表,还有 deleted,均是用在触发器的,详细介绍请看:

http://tech.ccidnet.com/art/1106/20080310/1384913_1.html