Multi-query index tuningSpecialty manufacturer, anonymized × red9CS-0147
A shipping-release query stopped reading 42,545 pages a run and started reading 67
The problem. A specialty manufacturer brought us three slow statements on its production database. The headline one, a payment-authorization and shipping-release query running about 10,000 times a day, averaged 304 ms and 42,545 reads. A cycle-count query ran roughly 70,000 times a day, and a document-line query took 29 seconds when it ran at all.
What we did. We built four indexes across the three statements, keyed to each predicate with the output columns included, and captured before-and-after figures in Query Store for all of them. This is a case where reads mattered more than the stopwatch: the duration improved five-fold while the IO improved 635-fold.
Red9 · Performance Impact
Reads per run
635x
42,545 pages down to 67 on the headline query.
Per-run duration
304 → 65 ms
About 40 minutes a day back at 10,000 runs.
Statements tuned
3
Four indexes in total, each shipped with the script that reverses it.
Duration
304 ms → 65 ms
4.7x shorter
Reads
42,545 → 67
635x fewer
Cycle-count query
278 → 32 reads
~70,000 / day
Shipping release
reads, per run
How to read these. The 635x is 42,545 reads over 67 on the headline statement. The 40 minutes a day is roughly 10,000 runs at the 239 ms each one stopped taking. The second statement, at about 70,000 runs a day, went from 60 ms and 278 reads to 40 ms and 32. Every figure came out of the manufacturer's Query Store, before the indexes and after.
The result. The shipping-release path stopped hammering the disks: 67 pages a run instead of 42,545, and 65 ms instead of 304. The cycle-count query, which fires seven times as often, got cheaper at the same time, and the third statement dropped from 29 seconds to 8.
The technical detail
What the review found. The authorization table had no index on its status columns, so the release query scanned it and then joined out to a header table on a date column that was also unindexed. The cycle-count and document-line statements had the same shape of gap on their own tables.
What we changed (identifiers generalized for privacy):
-- Query #1 shipping release: ~10,000x/day, 304 ms, 42,545 reads.
CREATE NONCLUSTERED INDEX IX_Red9_CreditCardAuth_Status
ON dbo.[credit_card_auth] (statusActive, authorizedCaptured, voided);
CREATE NONCLUSTERED INDEX IX_Red9_ReleaseHeader_LastShipDate
ON dbo.[release_header] (lastShipDate) INCLUDE (releaseId, orderId);
-- Query #2 cycle count and Query #3 document line: two further indexes
-- rollback: DROP INDEX scripts provided for all four
Once each predicate had an index behind it, the release query read 67 pages instead of 42,545 and returned in 65 ms.