【问题标题】:Validation trigger not working in SQL Server验证触发器在 SQL Server 中不起作用
【发布时间】:2022-01-17 05:00:59
【问题描述】:

我正在为一个班级项目做一个触发器。

我有一个名为salesman 的表,其中有一列名为type。表中的所有员工当前拥有typefullparttemp

触发器的目的是检查新插入的行的type 并确保它与已经存在的三个行之一匹配。如果是,它应该允许插入,如果不是,它应该停止它或将它回滚。感谢您提出任何想法!

这是代码的最新排列。

CREATE TRIGGER trg_validate_emp_type
ON salesman
FOR INSERT
AS
BEGIN
    DECLARE @job varchar (20)

    SELECT @job = (SELECT type FROM inserted)

    IF NOT EXISTS (SELECT 1 FROM salesman WHERE UPPER(type) = UPPER(@job))      
    BEGIN
        ROLLBACK TRANSACTION
        PRINT 'employee type not valid'
    END
END

【问题讨论】:

  • 假设 INSERT 只包含一行;这是不正确的。 INSERT 可以包含 0+ 行。见The Silent Bug I Find in Most Triggers
  • 您甚至不应该在这里考虑触发器,这就是存在 检查约束 的原因。
  • 你说“确保它与已经存在的三种类型中的一种匹配”这三种类型是从哪里来的?
  • 这是一个 AFTER 触发器。所以该行已经被插入了,所以它总是“有效的”。
  • 出于多种原因,请勿在生产系统的触发器中使用 PRINT。为了表明发生了错误,您 THROW 提供了适当的信息(忽略在这种情况下首选约束的事实)。

标签: sql sql-server tsql triggers


【解决方案1】:

正如一些人指出的那样,这看起来像是检查约束或外键的工作。

例如

create table salesman(id int primary key, type varchar(20) check (type in ('a','b')))

create table salesman_type (type varchar(20) primary key )
create table salesman(id int primary key, type varchar(20) references salesman_type)

但如果您确实想编写一个触发器来阻止插入表中不存在的值,您可以这样做:

    use tempdb
    go
    create table salesman
    (
      id int identity primary key, 
      type varchar(20) not null, 
      index ix_salesman_type(type) 
    )
    insert into salesman(id,type) values (1,'a')
    insert into salesman(id,type) values (2,'b')
    go
    CREATE OR ALTER TRIGGER trg_validate_emp_type
    ON salesman
    FOR INSERT
    AS
    BEGIN
    
        IF EXISTS 
        (
          SELECT * 
          FROM inserted 
          WHERE type not in
              (
                select type 
                from salesman
                where id not in (select id from inserted)
              )
        )
        BEGIN
           throw 50001, 'employee type not valid', 1;
        END
    END

go

insert into salesman(id,type) values (3,'c') -- fails

insert into salesman(id,type) values (3,'a') -- succeeds 

由于 XACT_ABORT 在触发器中默认为 ON,因此您可以简单地 THROW 而不是 ROLLBACK + PRINT,并且客户端会收到更好的错误消息。

【讨论】:

  • 谢谢你,这工作得很好。既然我知道在现实世界中这不是最好的方法,你介意解释一下第二个表创建代码的第二行吗?我们以前没有见过“引用”声明。
  • 这是声明外键约束的快捷方式。
【解决方案2】:

如果要检查列的值,有更有效的方法。 最佳实践是定义约束。 当值存储在表中时,使用 FOREIGN KEY 约束;在其他情况下使用 CHECK 约束。

【讨论】:

  • 要成为一个好的答案,您需要提供代码来创建满足 OP 需求的检查约束。
猜你喜欢
  • 1970-01-01
  • 2016-09-11
  • 2013-10-07
  • 2013-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多