【问题标题】:SQL trigger for audit table getting out of sync审计表不同步的 SQL 触发器
【发布时间】:2014-03-07 13:07:36
【问题描述】:

我最近创建了一个 SQL 触发器来替换我曾经运行的非常昂贵的查询,以减少我的数据库每天执行的更新量。

在执行更新之前,我会检查当天已经发生了多少更新,这曾经通过查询来完成:

SELECT COUNT(*) FROM Movies WHERE DateAdded = Date.Now

我的数据库有超过 100 万条记录,这个查询每分钟运行大约 1-2k,所以你可以明白我为什么要为此采取新方法。

所以我创建了一个审计表并设置了一个 SQL 触发器,以在 Movie 表上发生任何 INSERT 或 UPDATE 时更新此表。但是我注意到审计表每天有几百个不同步(审计表计数高于电影表中的实际更新)。由于这不会造成大问题,我只是好奇是什么原因造成的,或者如何调试它?

SQL 触发器:

ALTER TRIGGER [dbo].[trg_Audit]
ON [dbo].[Movies]
AFTER UPDATE, INSERT
AS
BEGIN
    UPDATE Audit SET [count] = [count] + 1 WHERE [date] = CONVERT (date, GETDATE())
    IF @@ROWCOUNT=0
    INSERT INTO audit ([date], [count]) VALUES (GETDATE(), 1)
END

上述触发器仅在 Movie 表上的 UPDATE 或 INSERT 之后发生,并尝试更新 Audit 表中的 count + 1,如果它不存在 (IF @@ROWCOUNT=0),则创建它。任何帮助将非常感激!谢谢。

【问题讨论】:

  • 您是否意识到,如果您一次更新 10 条记录,此触发器只会将计数增加 1?
  • @OGHaza 很好,因为我一次从不更新或插入多个记录,而且审计表计数(同步问题)高于我查询电影表时返回的值与 WHERE DateAdded = Date.Now... 这就是我想要弄清楚的
  • 这意味着我的触发器被触发的次数超过了数据库的实际更新/插入量。我还进行了测试,以确保当我创建和 #tempTables 时它没有触发,也不是那样。
  • 那么在这种情况下,您的电影表必须有一些额外的更新......
  • 当使用 AFTER UPDATE, INSERT 是否意味着它只会在成功更新/插入正确后触发?

标签: sql-server tsql triggers counter audit-logging


【解决方案1】:

与其将计数增加 1,不如将其增加例如已更改的记录数。

UPDATE Audit 
SET [count] = [count] + (SELECT COUNT(*) FROM INSERTED)
WHERE [date] = CONVERT (date, GETDATE())
IF @@ROWCOUNT=0
INSERT INTO audit ([date], [count]) 
VALUES (GETDATE(), (SELECT COUNT(*) FROM INSERTED))

【讨论】:

  • 我看不出其他任何问题,所以在您尝试之前,我只能假设就是这样。该触发器似乎在 SQLFiddle 上工作正常。
【解决方案2】:

这样的事情应该可以工作:

create table dbo.Movies (
    A int not null,
    B int not null,
    DateAdded datetime not null
)
go
create view dbo.audit
with schemabinding
as
    select CONVERT(date,DateAdded) as dt,COUNT_BIG(*) as cnt
    from dbo.Movies
    group by CONVERT(date,DateAdded)
go
create unique clustered index IX_MovieCounts on dbo.audit (dt)

这称为indexed view。优点是 SQL Server 负责维护存储在此视图中的数据,并且始终正确。

除非您使用的是 Enterprise/Developer 版本,否则您将使用 NOEXPAND 提示查询 audit 视图:

SELECT * from audit with (noexpand)

这样做的好处是

a) 您现在不必自己编写触发器(SQL Server 实际上确实有一些与幕后触发器非常相似的东西),

b) 它现在可以处理多行插入、更新和删除,并且

c) 您不必编写逻辑来处理更改 DateAdded 值的更新。

【讨论】:

  • 嘿,Damien,我刚刚在我的开发环境中进行了测试,它似乎可以正常工作和更新,但是我想知道这是否会比触发功能有更多的开销?如您所见,我必须在触发器中运行一些技巧,以避免在尝试更新之前检查行是否存在的 select 语句。由于我每天收到超过 1500 万个请求,因此我需要确保不会大幅增加我的服务器负载。这些视图何时更新?一经请求?还是喜欢每次插入/更新后的触发器?
  • @bfritz - 它们几乎就像您编写触发器一样,并且围绕索引视图中允许的规则是为了确保它们是有效的 - 即它们是经过设计的,显式地,以便可以纯粹根据受特定语句影响的行来维护索引 - 他们永远不必扫描整个表。
  • 只是想补充一下,我在生产中实现了这个,发现当不使用“WITH(NOEXPAND)”时,查询速度很慢!再次感谢您!
  • @bfritz - 是的,没有 noexpand 提示,它基本上会忽略您所做的所有工作并在整个基表上运行查询,而忽略索引。这是 MS 试图向您推销企业版的方式之一。
猜你喜欢
  • 1970-01-01
  • 2014-04-19
  • 2013-06-20
  • 2021-06-05
  • 2017-10-08
  • 2018-10-24
  • 2016-09-18
  • 2016-11-06
相关资源
最近更新 更多