【问题标题】:MSSQL Trigger - IssueMSSQL 触发器 - 问题
【发布时间】:2014-02-06 09:25:50
【问题描述】:

我在一个表上有一个 SQL 触发器,它将在插入、更新和删除之后触发。

我将所有受影响的记录插入到一​​个单独的物理表中,其中包含定义更新状态的代码。下面的代码 sn -p 是定义的触发器。

CREATE TRIGGER [dbo].[DATA_CACHE]
   ON  [dbo].[DATA_USAGE] 
for Insert,Update,Delete
AS 
BEGIN
    if(select COUNT(*) from inserted)>0
    begin
        if (select COUNT(*) from deleted)>0
        BEGIN
            --update
            INSERT INTO CACHE_UPDATE_TABLE (CODE, ID, DATE, COUNT)
            SELECT 2, ins.ID, ins.DATE, ins.COUNT
            from inserted ins
        END
        else
        begin
            -- insert
            INSERT INTO  CACHE_UPDATE_TABLE (CODE, ID, DATE, COUNT)
            SELECT 1, ins.ID, ins.DATE, ins.COUNT
            from inserted ins
        end
    END
    else 
    BEGIN
        -- delete
        INSERT INTO  CACHE_UPDATE_TABLE (CODE, ID, DATE, COUNT)
        SELECT 3, del.ID, del.DATE, del.COUNT
        from deleted del
    end
END
SELECT * FROM CACHE_UPDATE_TABLE

正如您在上面的触发器中看到的,我在 MISTAKE 触发器之后添加了一条附加语句,从目标表中选择所有值。该语句在定义的触发器之后,但是当我尝试更改触发器时,通过右键单击触发器并选择修改,它还向我显示了触发器结束块之后的 select 语句。

这是否意味着每次触发触发器时都会执行此选择语句?这是我的第一个问题(问题 A)- 可能是一个愚蠢的问题,但我对此有点困惑。

我的第二个问题是(问题 B)我在 CACHE_UPDATE_TABLE 上遇到锁定问题,这可能是锁定的原因吗?还有一个 SQL 作业每隔一分钟运行一次以检查 CACHE_UPDATE_TABLE 表,然后我执行一些操作(与链接服务器相关)并在完成后从 CACHE_UPDATE_TABLE 中删除这些记录。锁定问题可能是因为这个?如果是这样,我该如何应对?

我的第三个问题是(问题 C)这是使用触发器执行此操作的最佳方式还是我可以通过其他方式执行此操作吗?触发器定义是否正确?

-任何帮助将不胜感激...谢谢。

【问题讨论】:

  • 你有很多问题,这就是为什么你没有收到任何答案。例如,我可以部分回答 A,完全回答 C,但不能回答 B。

标签: sql-server tsql triggers


【解决方案1】:

你有很多不同的问题,这可能是你没有收到任何答案的原因,但我会尽我所能。

A) 这实际上是一个非常有趣的问题。我会假设它什么都不做 - 它会在您创建触发器时执行,但不会成为触发器的一部分 - 但是我之前注意到这个奇怪的行为,所以我用一个简单的存储过程进行了测试:

CREATE PROCEDURE dbo.test ( @i INT ) AS
BEGIN
    SELECT @i  
END;
SELECT 'hi'
GO

执行存储过程会导致SELECT 'hi'SELECT @i 被触发。对于您的问题,我仍然没有答案,但我绝对会确保在您仅出于这个原因创建触发器时不会在触发器之外有任何杂散的 SQL。

我刚刚对此进行了更多调查,显然存储过程的结尾是第一个GO 在过程之后的任何地方(如果您不使用,SQL Server 会自动添加到末尾)。所以你可以在END之后定义你的整个过程——你仍然可以使用参数。

这似乎是因为BEGINEND 不是存储过程定义的必需部分——它们实际上并不表示存储过程的开始和结束,它们只是一个不相关的@ 987654328@ 块就像你可能放在和IF 语句之后。您可以在过程定义中拥有任意数量的BEGIN...END 块,或者根本没有。

C) 我肯定会改变你的触发器。通过组合 3 个触发器而不重用任何代码,您已经大大复杂化了它。组合INSERT,UPDATEDELETE 触发器的唯一原因是您不必重复代码。您应该:

  • 有 3 个单独的触发器,每个触发器仅包含相关的 INSERT - 这样您就可以删除所有条件逻辑。
  • 将它们放在一起,但使用一些条件逻辑仅计算出 CODE,并且只有 1 个 INSERT 语句。

我很想使用 3 个单独的触发器,或者至少一个单独的删除触发器,然后将 CASE del.ID IS NULL THEN 1 ELSE 2 END 用于 CODE 上的 INSERT/UPDATE 触发器。但是您可以将它们与(未经测试的)结合使用:

INSERT INTO  CACHE_UPDATE_TABLE (CODE, ID, DATE, COUNT)
SELECT CASE WHEN del.ID IS NULL THEN 1
            WHEN ins.ID IS NULL THEN 3
            ELSE 2 END
    ,ISNULL(ins.ID, del.ID)
    ,ISNULL(ins.DATE, del.DATE)
    ,ISNULL(ins.COUNT, del.COUNT)
FROM deleted del
FULL OUTER JOIN inserted ins ON del.ID = ins.ID

【讨论】:

  • 感谢您的回复。加入触发器似乎仍然很昂贵。在我的触发器中,我只有两个条件,每次都会检查并且插入将通过。就性能而言,我认为这不会很昂贵。我可能错了,我会测试一下。但是,是的,您的程序的反应方式很奇怪,无论您添加什么,在 END 语句之后,它似乎都被执行了。
  • 在这些情况下都不会很昂贵,但是IF 语句会阻止缓存好的查询计划,所以我敢打赌JOIN 会表现得更好 - 只有知道的方法会虽然是基准。最好的性能无疑来自 3 个独立的触发器,它们都缓存了理想的执行计划。
  • 用关于 A 的更多信息编辑了我的答案
【解决方案2】:

删除那个

SELECT * FROM CACHE_UPDATE_TABLE

【讨论】:

  • 老兄,我已经删除了它...我想知道为什么它在触发器之外被执行。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-06-05
  • 2011-08-06
  • 2021-03-27
  • 2021-08-23
  • 2011-05-19
  • 2011-03-17
  • 2014-10-02
相关资源
最近更新 更多