Reducing Trigger Executions
I’ve never been a fan of triggers. I don’t like the idea of them adding an additional tax on every operation. It’s easy to forget they are even there, consuming your cycles. I’ve even seen a few nasty death-by-a-thousand-cuts scenarios with them. But I found something out this week that makes me like them even less.
I was tuning a procedure that runs 284 million times a day.
Over a number of servers and databases, but yes, that number is correct. It takes 2.5ms to run on average, with 1.0ms of CPU time. I’ll spare you the math, but that means over 3 cores of SQL Server are doing nothing but running this procedure 24/7/365. Anything we can do to improve this will be significant, even if we just shave off half a millisecond.
Small Improvements
The procedure is attempting to update or insert a few records based on a TVP input. Sometimes we run an UPDATE statement, but update the column to its current value based on one input parameter. Based on another parameter, we may filter out all rows and update nothing. But in both cases, even though we haven’t changed any data we still took the time to run the UPDATE statement. I’m unsure how often those two parameters match one of those two checks, but if we check them first we can entirely skip the UPDATE.
Like I said, every little bit helps.
After the UPDATE, there is an INSERT to make sure if we didn’t UPDATE the row because it doesn’t exist, we INSERT it. There are conditionals around the UPDATE, but not the INSERT. So, we run it every time we run the proc. 284 million times a day. Even if there’s nothing to insert.
Trigger Happy
The revelation is this:
“These triggers fire when any valid event fires, whether table rows are affected or not.”
Ouch. And I really wasn’t aware of this. So, we pay for the INSERT execution every time, even if we don’t need to insert anything. And the INSERT trigger on that table fires as well, trying to update another table even though the INSERTED and DELETED tables are empty. Charming.
But if our TVP had x items to update\insert and our UPDATE statement handled y of them, we only need to try the INSERT if y<x.
We won’t even need to query the underlying table or the TVP; just check the row counts.
We’re likely to update these records many times, and we’ll obviously only insert them once. Checking the execution counts for the INSERT and UPDATE triggers and subtracting the number of executions from this proc, it seems we UPDATE the table several hundred times for each INSERT. So, we should be able to reduce trigger executions by a huge amount.
Well then. I’ll look forward to seeing those executions drop shortly.
If you liked this post, please follow me on twitter or contact me if you have questions.
Postscript
This change reduced trigger executions and INSERT statement executions from ~350 million per day, to ~5 million.
Graphs like this make me happy. 🙂
1 thought on “Reducing Trigger Executions”