2012-04-09 48 views
10

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

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

+0

什么是“预检”的本质是什么?触发器确实有一个管理'插入'/'删除'表的开销。是否可以用另一种方式强制执行? – 2012-04-09 11:46:17

+0

我们需要的是三元组(3列)上的唯一约束,忽略一列中的空字符串。 – Cartesius00 2012-04-09 11:47:38

+0

因此,对于元组'(a,b,c)'如果'c'的值为空字符串,为了唯一约束的目的,您希望完全忽略该元组? – 2012-04-09 11:49:49

回答

9

是的。

您确实需要写明确的INSERTUPDATE

触发器运行INSTEAD OF DML操作。如果将触发器留空,则除INSERTED/DELETED表格被创建并填充到tempdb以外,不会执行任何操作。

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

+0

谢谢。那些DML操作中插入和更新行的别名是什么? – Cartesius00 2012-04-09 11:46:23

+0

@詹姆斯 - “INSERTED”和“DELETED”,但这些表不是行。触发器每声明触发一次。所以'INSERT INTO YourTable SELECT * FROM INSERTED'将是一个典型的'INSTEAD OF INSERT'触发器。 – 2012-04-09 11:46:57

+0

如果'YourTable'包含标识列和许多其他许多列,会怎么样?那么'SELECT *'是一个问题,不是吗? – Cartesius00 2012-04-09 11:49:27

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