【问题标题】:Trigger that excludes some words排除某些单词的触发器
【发布时间】:2019-09-19 08:26:10
【问题描述】:

我有一个与 T-SQL 中的数据库触发器相关的问题。

我想创建一个触发器,只允许特定单词(可用,不可用)到列avalability

怎么做?

我了解触发器是什么,但在命令序列中迷失了 - 什么应该在什么之后。

【问题讨论】:

  • 不确定您是否真的需要触发器,为什么不在相关列上创建CHECK 约束?
  • 您需要一个before insert 触发器。通过 Goggle 有很多例子。或者@GMB 的建议。
  • 这听起来绝对像是您在描述 CHECK CONSTRAINT 而不是 TRIGGER
  • 触发器绝对是这项工作的错误工具。使用检查约束。

标签: sql sql-server tsql database-trigger check-constraints


【解决方案1】:

不确定这里是否真的需要触发器。另一种解决方案是在相关列上创建CHECK 约束,例如:

ALTER TABLE TableName
ADD CONSTRAINT availability_check CHECK (availability IN ('available', 'not available'));

Demo on DB Fiddle

CREATE TABLE TableName (availability VARCHAR(20) );

ALTER TABLE TableName
ADD CONSTRAINT availability_check CHECK (availability IN ('available', 'not available'));

INSERT INTO TableName(availability) VALUES('foo');

-- Error
Msg 547 Level 16 State 0 Line 1
The INSERT statement conflicted with the CHECK constraint "availability_check". The conflict occurred in database "fiddle_325b8b256056466caf52aa4bb7467fd7", table "dbo.TableName", column 'availability'.
Msg 3621 Level 0 State 0 Line 1
The statement has been terminated.

【讨论】:

    【解决方案2】:

    你可以ALTER你的表并直接添加CHECK Constraint

    ALTER TABLE YourTableName
    ADD CONSTRAINT CHK_Avalability CHECK(Avalability IN('Available', 'Not Available'));
    

    请注意,如果表中已经插入了一些数据并且与CHECK Constraint不匹配和冲突,这将引发错误。

    如果你还没有CREATE 你的桌子,那么你可以使用

    CREATE TABLE YourTableName(
      Availability VARCHAR(20) NOT NULL
        CONSTRAINT CHK_Avalability CHECK(Availability IN('Available', 'Not Available')) 
    );
    

    【讨论】:

      【解决方案3】:

      有两种方法可以解决这个问题。

      1. 追求:基于检查约束的方法:您可以简单地使用检查约束来实现它。正如@GMB 在他的回答中已经提到的那样,它使工作变得更简单。
      ALTER TABLE Table1 ADD CONSTRAINT CHK_Table1_Availability CHECK (Availability IN ('Available', 'Not Available'))
      
      1. 不要选择:基于触发器的方法:如果您想创建触发器,您可以创建 INSTEAD OF TRIGGER,如下所示。此外,您还应该创建类似的 INSTEAD OF UPDATE 触发器,以处理 UPDATE 场景。但是,这是一种迂回的方法。查看所需的代码量,相应的维护。
          CREATE TRIGGER [dbo].[tr_availability_insert]
              ON [dbo].[Table1]
              INSTEAD OF INSERT
          AS
          BEGIN
          BEGIN TRY
          BEGIN TRANSACTION
          SET NOCOUNT ON;
      
          IF (inserted.Availability IN ('available', 'not available'))
          BEGIN 
          INSERT INTO Table1(Id, Availability)
          SELECT Id, Availability FROM inserted
          END
      
          COMMIT;
          END TRY
          BEGIN CATCH
      
          IF @@TRANCOUNT > 0
               ROLLBACK
      
          RETURN;
          END CATCH
      
      CREATE TRIGGER [dbo].[tr_availability_update]
              ON [dbo].[Table1]
              INSTEAD OF UPDATE
          AS
          BEGIN
          BEGIN TRY
          BEGIN TRANSACTION
          SET NOCOUNT ON;
      
          IF (inserted.Availability IN ('available', 'not available'))
          BEGIN 
          UPDATE Table1
          SET Availability = inserted.Availability
          WHERE Table1.Id = inserted.Id;
          END
      
          COMMIT;
          END TRY
          BEGIN CATCH
      
          IF @@TRANCOUNT > 0
               ROLLBACK
      
          RETURN;
          END CATCH
      

      【讨论】:

      • 可以使用触发器,但是: 1. 当用户尝试更新列时,您必须编写类似的代码。 2. 您必须为触发器内的表中的所有列提供插入/更新语句。这不是一个可怕的问题,但它使数据库的维护变得更加困难。
      • @ZoharPeled,同意你的看法。我只是提供了代码,以便他了解所涉及的工作量。我还提到他必须再次编写 INSTEAD OF UPDATE 触发器。我自己只推荐 CHECK Constraint。
      • @ZoharPeled,我已经更新了我的答案以澄清所需的工作量
      • IF (inserted.Availability IN ('available', 'not available') 有不平衡的括号,不清楚它如何检查inserted 中的所有行。 UPDATE Table1(Availability) SET Availability = inserted.Availability 对于它如何从inserted 中选择数据也有点神秘,但至少它会更新表中的所有 行,因为没有where 子句。
      • @HABO,感谢您发现问题。纠正了他们。我只想提一下这项工作的复杂性。 OP 没有提供表架构和所有内容。刚刚提到了努力。
      【解决方案4】:

      CHECK 约束是我要走的路。另一种方法是使用引用约束,AKA 主键和外键。如果在多个地方使用“可用”和“不可用”,这将比 CHECK 约束更好,因为您可以在多个表中强制执行相同的规则。通过这种方式,您可以强制保持一致性并避免某些表具有其他值,例如“可用”和“不可用”。

      -- Lookup table for all possible "Availability" values
      CREATE TABLE dbo.LU_Availability
      (
        AvailabilityPK VARCHAR(14) NOT NULL,
        CONSTRAINT pk_LU_Availability PRIMARY KEY CLUSTERED(AvailabilityPK)
      );
      INSERT dbo.LU_Availability (AvailabilityPK) VALUES ('Available'),('Not Available');
      
      -- Table with the allowable values enforced via foriegn key constraint
      CREATE TABLE dbo.YourTable
      (
        someId         INT IDENTITY,
        someValue      CHAR(10),
        [Availability] VARCHAR(14) NOT NULL,
        CONSTRAINT fk_AvailabilityTxt FOREIGN KEY([Availability]) 
          REFERENCES dbo.LU_Availability(AvailabilityPK)
      );
      
      INSERT dbo.YourTable ([Availability]) VALUES ('Available');     -- Succeeds
      INSERT dbo.YourTable ([Availability]) VALUES ('NOT Available'); -- Succeeds
      INSERT dbo.YourTable ([Availability]) VALUES ('Perhaps');       -- Fails
      

      最后 - 重要的是要了解,虽然约束(CHECK、PK、FK、UNIQUE)会稍微减慢速度,但触发器是性能杀手。这就是为什么有经验的开发人员和 DBA 建议在约束可以解决问题时避免使用它们。

      【讨论】:

        猜你喜欢
        • 2012-02-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多