T-SQL触发器 - 防止重复,但并非总是如此

时间:2016-02-10 09:21:11

标签: sql-server tsql

我想避免可能复制字段值的INSERTS和UPDATES,但并非总是如此。

我有一个varchar字段 Cat_Catalog 。在表目录

我可以有两行 Cat_Catalog 的值“123”重复,但我不能有重复字段 Cat_Catalog 以“KAT”字开头(所以我不能有两行“KAT123”Cat_Catalog的值)

我所做的以下触发器无法正常工作,因为将要更新的字段以KAT触发器开始始终引发错误(变量@IfExist始终返回true - 这可能是因为AFTER UPDATE,INSERT语法)。

我想避免使用INSTEAD OF语法,因为更新是由某些API生成的,我没有文档,如果值不以'KAT'开头,我不知道该怎么办。

GO
/****** Object:  Trigger [dbo].[Catalog_InsertUpdateCatalog]  ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

--If first three characters are 'KAT' 
--then check for duplicate and raiseerror

ALTER TRIGGER [dbo].[Catalog_InsertUpdateCatalog] ON [dbo].[Catalog]
FOR UPDATE,INSERT
AS
set nocount on;

DECLARE @CatalogInsert varchar(50)
DECLARE @IfKat varchar(10) = 'FALSE'
DECLARE @IfExist varchar(10) = 'FALSE'

SELECT @CatalogInsert = Cat_Catalog
FROM inserted

--Does It starts with 'KAT' ?
IF (@CatalogInsert like 'KAT%')
BEGIN
    SET @IfKat = 'TRUE'
END

--Check for Duplicate
IF EXISTS(
            Select * from Test.dbo.Catalog t
            where t.Cat_Catalog = @CatalogInsert
        )
        BEGIN
            SET @IfExist = 'TRUE'
        END

IF ( @IfExist = 'TRUE' and @IfKat = 'TRUE' )
    BEGIN
        RAISERROR ('Catalog allready exists: %s , ISKAT:%s , EXIST:%s', 16, 1, @CatalogInsert, @IfKat, @IfExist);
    END 

`

问题是我不知道如何检查目录表中已存在的当前值已经存在(必须在更新前检查)。

4 个答案:

答案 0 :(得分:1)

请尝试 新版

    ALTER TRIGGER [dbo].[Catalog_InsertUpdateCatalog] ON [dbo].[Catalog]
    FOR UPDATE,INSERT 
    AS BEGIN
      set nocount on;
      --Check for Duplicate
      IF EXISTS(
         Select 1 
         From (
            -- Updated
            SELECT 
              COUNT(*) OVER (PARTITION BY Cat_Catalog) Cnt, 
              Cat_Catalog  
            FROM dbo.Catalog 
            WHERE 
               Cat_Catalog LIKE 'KAT%'
         ) t
         Join inserted i ON t.Cat_Catalog = i.Cat_Catalog AND Cnt > 1
      )
      BEGIN
         RAISERROR ('Catalog allready exists!', 16, 1);
         ROLLBACK TRANSACTION;
      END

    END 

此触发器使用与插入(或更新)行中相同的[Cat_Catalog]字段检查行是否存在。如果存在重复行且[Cat_Catalog]以' KAT'触发RAISE错误+回滚事务。

更新:我改变了触发器。它现在应该正常工作(我测试它)。触发FOR UPDATE,INSERT在表中发生更改后触发。所以我们需要检查表中的重复行。
我通过COUNT(*)OVER(由Cat_Catalog分区)来完成它,但你可以用更熟悉的方式检查它:

    SELECT 
        COUNT(*) Cnt, 
        Cat_Catalog  
    FROM 
        dbo._Catalog 
    WHERE 
        Cat_Catalog LIKE 'KAT%'
    GROUP BY
        Cat_Catalog

答案 1 :(得分:1)

使用CHECK constraint而不是触发器将是一个更好的解决方案,因为触发器在事务中执行得更晚,最终的回滚可能会很昂贵。您可以按如下方式定义检查约束:

[{"date":1454284800,"exercises":[{"name":"Tricep Skull-Crushers","sets":"5","reps":"12","time":"20","weight":"","notes":"testing testing again testing this app which is great "},{"name":"Barbell Squat","sets":"","reps":"","time":"","weight":"","notes":""}]},{"date":1454284800,"exercises":[{"name":"Tricep Skull-Crushers","sets":"5","reps":"12","time":"20","weight":"","notes":"testing testing again testing this app which is great "},{"name":"Barbell Squat","sets":"","reps":"","time":"","weight":"","notes":""},{"name":"Arnold Press","sets":"5","reps":"12","time":"","weight":"","notes":""}]},{"date":1454112000,"exercises":[{"name":"Single Arm Row","sets":"3","reps":"10","time":"","weight":"","notes":""},{"name":"Tricep Rope Pull-Downs","sets":"3","reps":"10","time":"","weight":"","notes":""}]},{"date":1454112000,"exercises":[{"name":"Single Arm Row","sets":"3","reps":"10","time":"","weight":"","notes":""},{"name":"Tricep Rope Pull-Downs","sets":"3","reps":"10","time":"","weight":"","notes":""}]},{"date":1454112000,"exercises":[{"name":"Single Arm Row","sets":"3","reps":"10","time":"","weight":"","notes":""},{"name":"Tricep Rope Pull-Downs","sets":"3","reps":"10","time":"","weight":"","notes":""}]}] 

请记住,如果表中已经有重复的“KATxxx”值,则必须删除它们或使用NOCHECK子句创建约束。

答案 2 :(得分:0)

在触发后使用而不是触发。

如果你的问题是反向触发,那么你需要使用NOT Eqals运算符来避免传递这样的错误指示。我应该说

      DECLARE @CATALOG_PK BIGINT;
      SELECT @CATALOG_PK = CATALOG_PK FROM INSERTED

  --Check for Duplicate
   IF EXISTS(
        Select * from Test.dbo.Catalog t
        where t.Cat_Catalog = @CatalogInsert
        AND CATALOG_PK <>@CATELOG_PK LIKE t.CATALOG LIKE 'KAT%' 
          )
    BEGIN
        SET @IfExist = 'TRUE'
    END

然而,如果多次更新或插入完成,这将失败。

答案 3 :(得分:0)

尝试以下两个触发器:

CREATE TRIGGER [MyCatalog_InsertCatalog] ON [dbo].[MyCatalog]
FOR INSERT
AS

IF EXISTS(
    SELECT I.* 
    FROM Inserted I 
    INNER JOIN MyCatalog M 
        ON M.Cat_Catalog = I.Cat_Catalog 
    WHERE I.Cat_Catalog  LIKE 'KAT%' )
BEGIN
    RAISERROR ('Insert Catalog already exists:  ', 16, 1);
    ROLLBACK TRANSACTION;
END

GO

CREATE TRIGGER [MyCatalog_UpdateCatalog] ON [dbo].[MyCatalog]
FOR UPDATE
AS

IF  EXISTS(
    SELECT I.* 
    FROM Inserted I 
    INNER JOIN MyCatalog M 
        ON M.Cat_Catalog = I.Cat_Catalog 
    INNER JOIN deleted d 
        ON d.Cat_Id = i.Cat_Id 
    WHERE I.Cat_Catalog LIKE 'KAT%' AND d.Cat_Catalog <> i.Cat_Catalog)
BEGIN

    RAISERROR ('Update Catalog already exists: ', 16, 1);
    ROLLBACK TRANSACTION;
END

修改

您可以将两个触发器组合成一个:

CREATE TRIGGER [MyCatalog_InsertUpdateCatalog] ON [dbo].[MyCatalog]
FOR INSERT, UPDATE
AS

IF  EXISTS(
    SELECT I.* 
    FROM Inserted I 
    INNER JOIN MyCatalog M 
        ON M.Cat_Catalog = I.Cat_Catalog 
    INNER JOIN deleted d 
        ON d.Cat_Id = i.Cat_Id 
    WHERE I.Cat_Catalog LIKE 'KAT%' AND d.Cat_Catalog <> i.Cat_Catalog)
BEGIN

    RAISERROR ('Update Catalog already exists: ', 16, 1);
    ROLLBACK TRANSACTION;
END
ELSE BEGIN
   IF EXISTS(
       SELECT I.* 
       FROM Inserted I 
       INNER JOIN MyCatalog M 
           ON M.Cat_Catalog = I.Cat_Catalog 
       WHERE I.Cat_Catalog  LIKE 'KAT%' )
   BEGIN
       RAISERROR ('Insert Catalog already exists:  ', 16, 1);
       ROLLBACK TRANSACTION;
   END
END