从触发器回滚事务

时间:2012-04-09 11:39:45

标签: sql sql-server-2008 sql-server-2008-r2

在MS SQL Server 2008 R2中,我们需要一个预插入和更新前触发器,用于检查某些内容并允许或回滚(通过raiserror)正在运行的插入/更新。

问题:在INSTEAD OF触发器中。是否真的必须明确地写入插入或更新?因为我们希望完成默认插入或更新,只进行“预先检查”。

2 个答案:

答案 0 :(得分:10)

您需要撰写明确的INSERTUPDATE

触发器运行INSTEAD OF DML操作。如果您将触发器留空,则除了INSERTED中正在创建和填充的DELETED / tempdb表之外,不会执行任何操作。

虽然根据评论中的讨论,我根本不会使用触发器,而是使用唯一的过滤索引CREATE UNIQUE INDEX ix ON T(a,b,c) WHERE c <> ''。在处理并发时,这可能更具性能并避免潜在的逻辑问题。

答案 1 :(得分:3)

除非您想要替换实际的插入或更新,否则您可能不需要INSTEAD OF触发器。在您的情况下,您需要FOR INSERT, UPDATE触发器。

当有人试图在titles表中添加或更改数据时,此示例触发器会向客户端输出消息。

USE pubs
IF EXISTS (SELECT name FROM sysobjects
      WHERE name = 'reminder' AND type = 'TR')
   DROP TRIGGER reminder
GO
CREATE TRIGGER reminder
ON titles
FOR INSERT, UPDATE 
AS RAISERROR ('inserts and updates to the titles table is not allowed', 16, 1)
GO

您也可以使用IF EXISTSCOLUMNS_UPDATED之类的内容。

这是另一个使用回滚的示例。

USE pubs
IF EXISTS (SELECT name FROM sysobjects
      WHERE name = 'employee_insupd' AND type = 'TR')
   DROP TRIGGER employee_insupd
GO
CREATE TRIGGER employee_insupd
ON employee
FOR INSERT, UPDATE
AS
/* Get the range of level for this job type from the jobs table. */
DECLARE @min_lvl tinyint,
   @max_lvl tinyint,
   @emp_lvl tinyint,
   @job_id smallint
SELECT @min_lvl = min_lvl, 
   @max_lvl = max_lvl, 
   @emp_lvl = i.job_lvl,
   @job_id = i.job_id
FROM employee e INNER JOIN inserted i ON e.emp_id = i.emp_id 
   JOIN jobs j ON j.job_id = i.job_id
IF (@job_id = 1) and (@emp_lvl <> 10) 
BEGIN
   RAISERROR ('Job id 1 expects the default level of 10.', 16, 1)
   ROLLBACK TRANSACTION
END
ELSE
IF NOT (@emp_lvl BETWEEN @min_lvl AND @max_lvl)
BEGIN
   RAISERROR ('The level for job_id:%d should be between %d and %d.',
      16, 1, @job_id, @min_lvl, @max_lvl)
   ROLLBACK TRANSACTION
END

我不确定您是否有交易,但在您的情况下,您会想要以下内容:

USE myDatabase

    IF EXISTS (SELECT name FROM sysobjects
          WHERE name = 'myTable' AND type = 'TR')
       DROP TRIGGER tr_myTrigger
    GO
    CREATE TRIGGER tr_myTrigger
    ON myTable
    FOR INSERT, UPDATE
    AS

if(exists(select * from inserted where rtrim(c) <> ''))
begin
  -- check to make sure the insert(s) are unique

    if(exists(
      select * from inserted i
      join dbo.myTable t on i.a = t.a and i.b = t.b and i.c = t.c)

    begin
      raiserror('Duplicate(s) found', 16, 1)
      rollback transaction
    end
end