-
Notifications
You must be signed in to change notification settings - Fork 52
Add nonclustered indexes for query/procedure/query store lookups #859
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1495,5 +1495,83 @@ BEGIN | |
| PRINT 'Created collect.server_properties table'; | ||
| END; | ||
|
|
||
| /* | ||
| Nonclustered indexes to support OUTER APPLY lookups in the Dashboard's | ||
| query/procedure/query store grid queries. Without these, the optimizer builds | ||
| an Eager Index Spool over the entire table to service the lookups, which can | ||
| take minutes on large installations (see #835). | ||
|
|
||
| ONLINE = ON is only supported on Enterprise/Developer/Azure editions. The | ||
| options string is built dynamically based on SERVERPROPERTY('EngineEdition'). | ||
| */ | ||
| DECLARE @online_option nvarchar(20) = | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: consider a Generated by Claude Code |
||
| CASE | ||
| WHEN CAST(SERVERPROPERTY(N'EngineEdition') AS integer) IN (3, 5, 8) | ||
| THEN N', ONLINE = ON' | ||
| ELSE N'' | ||
| END; | ||
|
|
||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Generated by Claude Code |
||
| DECLARE @index_sql nvarchar(max); | ||
|
|
||
| IF NOT EXISTS | ||
| ( | ||
| SELECT | ||
| 1/0 | ||
| FROM sys.indexes | ||
| WHERE object_id = OBJECT_ID(N'collect.query_stats') | ||
| AND name = N'IX_query_stats_hash_lookup' | ||
| ) | ||
| BEGIN | ||
| SET @index_sql = N' | ||
| CREATE NONCLUSTERED INDEX | ||
| IX_query_stats_hash_lookup | ||
| ON collect.query_stats | ||
| (query_hash, database_name, collection_time DESC) | ||
| WITH | ||
| (SORT_IN_TEMPDB = ON, DATA_COMPRESSION = PAGE' + @online_option + N');'; | ||
| EXEC sys.sp_executesql @index_sql; | ||
| PRINT 'Created collect.query_stats.IX_query_stats_hash_lookup index'; | ||
| END; | ||
|
|
||
| IF NOT EXISTS | ||
| ( | ||
| SELECT | ||
| 1/0 | ||
| FROM sys.indexes | ||
| WHERE object_id = OBJECT_ID(N'collect.procedure_stats') | ||
| AND name = N'IX_procedure_stats_name_lookup' | ||
| ) | ||
| BEGIN | ||
| SET @index_sql = N' | ||
| CREATE NONCLUSTERED INDEX | ||
| IX_procedure_stats_name_lookup | ||
| ON collect.procedure_stats | ||
| (database_name, schema_name, object_name, collection_time DESC) | ||
| WITH | ||
| (SORT_IN_TEMPDB = ON, DATA_COMPRESSION = PAGE' + @online_option + N');'; | ||
| EXEC sys.sp_executesql @index_sql; | ||
| PRINT 'Created collect.procedure_stats.IX_procedure_stats_name_lookup index'; | ||
| END; | ||
|
|
||
| IF NOT EXISTS | ||
| ( | ||
| SELECT | ||
| 1/0 | ||
| FROM sys.indexes | ||
| WHERE object_id = OBJECT_ID(N'collect.query_store_data') | ||
| AND name = N'IX_query_store_data_id_lookup' | ||
| ) | ||
| BEGIN | ||
| SET @index_sql = N' | ||
| CREATE NONCLUSTERED INDEX | ||
| IX_query_store_data_id_lookup | ||
| ON collect.query_store_data | ||
| (database_name, query_id, collection_time DESC) | ||
| WITH | ||
| (SORT_IN_TEMPDB = ON, DATA_COMPRESSION = PAGE' + @online_option + N');'; | ||
| EXEC sys.sp_executesql @index_sql; | ||
| PRINT 'Created collect.query_store_data.IX_query_store_data_id_lookup index'; | ||
| END; | ||
|
|
||
| PRINT 'All collection tables created successfully'; | ||
| GO | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| /* | ||
| Copyright 2026 Darling Data, LLC | ||
| https://www.erikdarling.com/ | ||
|
|
||
| Upgrade from 2.7.0 to 2.8.0 | ||
|
|
||
| Add nonclustered indexes on collect.query_stats, collect.procedure_stats, and | ||
| collect.query_store_data to support OUTER APPLY lookups in the Dashboard's | ||
| grid queries. Without these, the optimizer builds an Eager Index Spool over | ||
| the entire table to service the lookups, which can take minutes on large | ||
| installations (see #835). | ||
|
|
||
| ONLINE = ON is only supported on Enterprise/Developer/Azure editions. The | ||
| options string is built dynamically based on SERVERPROPERTY('EngineEdition'). | ||
| */ | ||
|
|
||
| SET ANSI_NULLS ON; | ||
| SET ANSI_PADDING ON; | ||
| SET ANSI_WARNINGS ON; | ||
| SET ARITHABORT ON; | ||
| SET CONCAT_NULL_YIELDS_NULL ON; | ||
| SET QUOTED_IDENTIFIER ON; | ||
| SET NUMERIC_ROUNDABORT OFF; | ||
| SET IMPLICIT_TRANSACTIONS OFF; | ||
| SET STATISTICS TIME, IO OFF; | ||
| GO | ||
|
|
||
| USE PerformanceMonitor; | ||
| GO | ||
|
|
||
| DECLARE @online_option nvarchar(20) = | ||
| CASE | ||
| WHEN CAST(SERVERPROPERTY(N'EngineEdition') AS integer) IN (3, 5, 8) | ||
| THEN N', ONLINE = ON' | ||
| ELSE N'' | ||
| END; | ||
|
|
||
| DECLARE @index_sql nvarchar(max); | ||
|
|
||
| IF NOT EXISTS | ||
| ( | ||
| SELECT | ||
| 1/0 | ||
| FROM sys.indexes | ||
| WHERE object_id = OBJECT_ID(N'collect.query_stats') | ||
| AND name = N'IX_query_stats_hash_lookup' | ||
| ) | ||
| BEGIN | ||
| SET @index_sql = N' | ||
| CREATE NONCLUSTERED INDEX | ||
| IX_query_stats_hash_lookup | ||
| ON collect.query_stats | ||
| (query_hash, database_name, collection_time DESC) | ||
| WITH | ||
| (SORT_IN_TEMPDB = ON, DATA_COMPRESSION = PAGE' + @online_option + N');'; | ||
| EXEC sys.sp_executesql @index_sql; | ||
| PRINT 'Created collect.query_stats.IX_query_stats_hash_lookup index'; | ||
| END; | ||
|
|
||
| IF NOT EXISTS | ||
| ( | ||
| SELECT | ||
| 1/0 | ||
| FROM sys.indexes | ||
| WHERE object_id = OBJECT_ID(N'collect.procedure_stats') | ||
| AND name = N'IX_procedure_stats_name_lookup' | ||
| ) | ||
| BEGIN | ||
| SET @index_sql = N' | ||
| CREATE NONCLUSTERED INDEX | ||
| IX_procedure_stats_name_lookup | ||
| ON collect.procedure_stats | ||
| (database_name, schema_name, object_name, collection_time DESC) | ||
| WITH | ||
| (SORT_IN_TEMPDB = ON, DATA_COMPRESSION = PAGE' + @online_option + N');'; | ||
| EXEC sys.sp_executesql @index_sql; | ||
| PRINT 'Created collect.procedure_stats.IX_procedure_stats_name_lookup index'; | ||
| END; | ||
|
|
||
| IF NOT EXISTS | ||
| ( | ||
| SELECT | ||
| 1/0 | ||
| FROM sys.indexes | ||
| WHERE object_id = OBJECT_ID(N'collect.query_store_data') | ||
| AND name = N'IX_query_store_data_id_lookup' | ||
| ) | ||
| BEGIN | ||
| SET @index_sql = N' | ||
| CREATE NONCLUSTERED INDEX | ||
| IX_query_store_data_id_lookup | ||
| ON collect.query_store_data | ||
| (database_name, query_id, collection_time DESC) | ||
| WITH | ||
| (SORT_IN_TEMPDB = ON, DATA_COMPRESSION = PAGE' + @online_option + N');'; | ||
| EXEC sys.sp_executesql @index_sql; | ||
| PRINT 'Created collect.query_store_data.IX_query_store_data_id_lookup index'; | ||
| END; | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Script is missing a trailing newline and a final Generated by Claude Code |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| 01_add_query_lookup_indexes.sql |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix looks right:
tr.query_hashnow staysbinary(8)through Phase 2/3 and only converts tonvarchar(20)in the final projection (line 1183), soqs2.query_hash = tr.query_hashis a native binary = binary comparison and sargable against the newIX_query_stats_hash_lookup. The symmetric fix is applied in the MCP copy of the query at lines 4441/4484. No other OUTER APPLYs in this file regressed (the remainingCONVERT(binary(8), @param, 1)forms at 2754/2923 are parameter→constant conversions, still sargable).Generated by Claude Code