Covering index at extreme frequencyHospitality software company, anonymized × red9CS-0236
A procedure called 78,236 times a day fell from 13.4 seconds to 0.62 seconds
The problem. A hospitality software company had a daily-revenue refresh procedure executing about 78,236 times a day at 13,403.5 ms each. Multiply those out and the statement accounted for roughly 291 hours of cumulative run time a day, which only fits inside a 24-hour day because dozens of copies of it were in flight at once. Each execution averaged 10,782.9 ms of processor time and 21,592,608 reads.
What we did. We captured the plan and Query Store history, then created a covering index keyed on the three columns the procedure filters, with the two status columns it returns included. Create and rollback scripts went over together and the before-and-after came from the same window.
Red9 · Performance Impact
SQL processing recovered
~278 hrs / day
From about 291 hours a day down to roughly 13.
Per-run duration
21.5x
13,403.5 ms down to 624.13 ms.
Reads per run
21,592,608 → 238,625
A 90-fold drop, on a procedure firing roughly once a second all day.
Duration
13,403.5 ms → 624.13 ms
21.5x shorter
CPU
10,782.9 ms → 537.27 ms
20x less
Executions
~78,236 / day
unchanged
Revenue refresh
duration, per run
How the math works. The 21.5x is 13,403.5 ms over 624.13 ms, and logical reads fell 90-fold; the report itself states 20.48x on duration and 89.49x on IO. The 278 hours a day comes from 78,236 executions multiplied by the 12.78 seconds each one stopped taking. Per-execution reads fell from 21,592,608 to 238,625. Every figure sits in the client's own Query Store captures.
The result. The refresh finishes in under two thirds of a second, and roughly 278 hours of cumulative daily run time came off the instance. On a server where one procedure had been crowding out everything else, that is the difference between a platform that keeps up and one that does not.
The technical detail
What the review found. The queue table behind the refresh was filtered on plan, month, and revenue-segment identifiers with no index matching that combination, so each of the ~78,236 daily executions scanned it and then looked up the two status columns it returned.
What we changed (identifiers generalized for privacy):
-- Revenue refresh procedure: ~78,236x/day, 13,403.5 ms avg, 21.6M reads/run.
CREATE NONCLUSTERED INDEX IX_RED9_tblQueuePlanningRefreshDailyRevenue
ON dbo.[tblQueuePlanningRefreshDailyRevenue] (planId, monthId, revSegId)
INCLUDE (statusDesc, statusId);
-- rollback: DROP INDEX IX_RED9_tblQueuePlanningRefreshDailyRevenue provided
With the filter keyed and the output included, the procedure answers from the index: 624.13 ms a run, 537.27 ms of CPU, 238,625 reads.