Question

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.

Was it helpful?

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.

OTHER TIPS

Rather than incrementing the count by 1 you should probably be incrementing it by the number of records that have changed e.g.

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))
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top