【问题标题】:Prevent duplicate data in using After Insert Trigger使用插入触发器后防止重复数据
【发布时间】:2021-04-05 00:10:15
【问题描述】:

我可以检测到重复记录,但是当我插入新数据时,即使不存在,它也会将其检测为重复记录。

这是我的代码:

ALTER TRIGGER [dbo].[SDPRawInventory_Dup_Trigger] 
ON [dbo].[SDPRawInventory]
AFTER INSERT  
AS 
    DECLARE @Year float, 
            @Month float, 
            @SDPGroup nvarchar(255);

    SELECT 
        @Year = i.InvYear, @Month = i.InvMonth, @SDPGroup = i.SDPGroup 
    FROM inserted i;

    IF (SELECT COUNT(*) FROM SDPRawInventory A
        WHERE A.InvYear = @Year
          AND A.InvMonth = @Month
          AND A.SDPGroup = @SDPGroup) >= 1
    BEGIN
        RAISERROR ('Duplicate data', 16, 1)
        ROLLBACK;
    END
    ELSE
    BEGIN
        INSERT INTO SDPRawInventory
            SELECT * FROM inserted;
    END

这是桌子

为了澄清没有主键也没有唯一标识符。

【问题讨论】:

  • 您的触发器存在基本 缺陷,您似乎认为它会每行调用一次 - 事实并非如此。触发器将每条语句触发一次,因此如果您的INSERT 导致此触发器触发插入25 行,您将触发触发器一次 和@987654325 @ 伪表将包含 25 行。您的代码将从Inserted 中选择这 25 行中的哪一行?它是不确定的,您将获得任意行,而您将忽略所有其他行。您需要重写触发器以考虑到这一点!
  • @marc_s 在防止重复记录的同时插入多行有什么想法吗?抱歉,我是使用触发器的新手。
  • 您为什么使用float 表示年份和月份?应该只是int
  • 一个简单的UNIQUE CONSTRAINT 绝对是处理这个问题的最简单和最有效的方法......

标签: sql sql-server tsql triggers


【解决方案1】:

如果您无法设置约束,那么您需要处理Inserted 可能有多个记录的事实。并且因为它是一个after insert 触发器,所以如果没有发现重复项,您无需执行任何操作,因为记录已经插入。

ALTER TRIGGER [dbo].[SDPRawInventory_Dup_Trigger] 
ON [dbo].[SDPRawInventory]
AFTER INSERT  
AS 
BEGIN
    SET NOCOUNT ON;

    IF EXISTS (
        SELECT 1
        FROM dbo.SDPRawInventory S
        INNER JOIN Inserted I ON
            -- Test for a duplicate
            S.InvYear = I.InvYear
            AND S.InvMonth = I.InvMonth
            AND S.SDPGroup = I.SDPGroup
            -- But ensure the duplicate is a *different* record - assumes a unique ID
            AND S.ID <> I.ID
    )
    BEGIN
        THROW 51000, 'Duplicate data.', 1; 
    END;
END;

注意简化和现代的错误处理。

编辑:如果您没有唯一键,也无权添加一个,那么您需要一个 instead of 触发器来仅插入非重复项,例如

ALTER TRIGGER [dbo].[SDPRawInventory_Dup_Trigger] 
ON [dbo].[SDPRawInventory]
INSTEAD OF INSERT  
AS 
BEGIN
    SET NOCOUNT ON;

    -- Reject the entire insert if a single duplicate exists
    -- Note if multiple records are inserted, some of which are duplicates and some of which aren't, they all get rejected
    IF EXISTS (
        SELECT 1
        FROM dbo.SDPRawInventory S
        INNER JOIN Inserted I ON
            -- Test for a duplicate
            A.InvYear = I.InvYear
            AND A.InvMonth = I.InvMonth
            AND A.SDPGroup = I.@SDPGroup
    )
    -- Test that Inserted itself doesn't contain duplicates
    OR EXISTS (SELECT 1 FROM Inserted GROUP BY InvYear, InvMonth, SDPGroup HAVING COUNT(*) > 1)
    BEGIN
        THROW 51000, 'Duplicate data.', 1; 
    END;

    INSERT INTO dbo.SDPRawInventory (SDP_SKU_DESC, WholeQty, InvYear, InvMonth, SDPGroup, invUOM, LooseQty)
      SELECT SDP_SKU_DESC, WholeQty, InvYear, InvMonth, SDPGroup, invUOM, LooseQty
      FROM Inserted I 
      WHERE NOT EXISTS (
          SELECT 1
          FROM dbo.SDPRawInventory S
          -- Test for a duplicate
          WHERE S.InvYear = I.InvYear
          AND S.InvMonth = I.InvMonth
          AND S.SDPGroup = I.SDPGroup
      );
END;

注意:这对处理现有的重复项没有任何作用。

【讨论】:

  • 在我现在工作的现有数据库中,根本没有约束,所以我很难拥有唯一的ID。
  • @princenhielcada:“SDPRawInventory”的当前主键是什么?
  • @princenhielcada:然后介绍一个。
  • @princenhielcada 我也添加了错误通知......只需注意它的全部或全部,因此如果Inserted 中存在单个重复项,它们都会被拒绝。
  • instead of 触发器不检查重复within inserted,即用户尝试使用单个insert 语句插入两个或多个重复行。
【解决方案2】:

此触发器在插入新记录之后执行,因此它至少会在SELECT COUNT 语句中找到原始记录。将&gt;= 1 更改为&gt;= 2 只能在保证一次插入一条记录时部分解决此问题。而且,如果新插入的记录在插入之前已经有多个重复记录在数据库中,它仍然会失败。

您需要从COUNT 中排除最新插入的记录。但更好的主意可能是添加一个UNIQUE 约束来防止重复,因此不需要触发器。

如果尚无法添加约束,则应启动清理过程以预先消除现有重复项。对我来说,其他一切看起来都存在很大缺陷,因为目前的方法不太可能使表格进入没有重复的状态。

【讨论】:

  • 问题是,现有的数据库根本没有任何约束。所以我对重复记录的解决方案是使用触发器。
  • @princenhielcada:您可能会考虑一个过程,首先清理重复项,然后添加唯一约束。
  • 我正在考虑使用存储过程来清理重复记录,但之后我需要使用触发器吗?怎么样?
  • @princenhielcada:当您可以事先删除重复项并添加 UNIQUE 约束时,您将不再需要触发器。但是,如果存在指向现有记录的现有外键引用,删除重复项可能会变得很棘手。
【解决方案3】:

您正在创建无限循环。您只需从触发器中移除插入部分即可。

ELSE
    BEGIN
         INSERT INTO SDPRawInventory
         SELECT * FROM inserted;
    END

这不应该在触发器中,因为触发器是作为插入的一部分调用的。您不应该在触发器中将实际插入写入表中。

【讨论】:

  • @Popeye 插入的目的是如果记录不存在则插入。
  • 不,实际上并没有调用触发器而不是插入。如果触发器不抛出任何错误,则插入将发生。不要明确写出来。
  • @Popeye 对此有何想法?我是这种情况的新手。
猜你喜欢
  • 1970-01-01
  • 2021-09-25
  • 1970-01-01
  • 2017-04-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-14
  • 1970-01-01
相关资源
最近更新 更多