Search code examples
sql-servert-sqltriggerscounteraudit-logging

SQL trigger for audit table getting out of sync


I recently created a SQL trigger to replace a very expensive query I used to run to reduce the amount of updates my database does each day.

Before I preform an update I check to see how many updates have already occurred for the day, this used to be done by querying:

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

Well my database has over 1 million records and this query is run about 1-2k a minute so you can see why I wanted to take a new approach for this.

So I created an audit table and setup a SQL Trigger to update this table when any INSERT or UPDATE happens on the Movie table. However I'm noticing the audit table is getting out of sync by a few hundred everyday (the audit table count is higher than the actual updates in the movie table). As this does not pose a huge issue I'm just curious what could be causing this or how to go about debugging it?

SQL Trigger:

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

The above trigger only happens after an UPDATE or INSERT on the Movie table and tries to update the count + 1 in the Audit table and if it doesn't exists (IF @@ROWCOUNT=0) it then creates it. Any help would be much appreciated! Thanks.


Solution

  • Something like this should work:

    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)
    

    This is called an indexed view. The advantage is that SQL Server takes responsibility for maintaining the data stored in this view, and it's always right.

    Unless you're on Enterprise/Developer edition, you'd query the audit view using the NOEXPAND hint:

    SELECT * from audit with (noexpand)
    

    This has the advantages that

    a) You don't have to write the triggers yourself now (SQL Server does actually have something quite similar to triggers behind the scenes),

    b) It can now cope with multi-row inserts, updates and deletes, and

    c) You don't have to write the logic to cope with an update that changes the DateAdded value.