diff --git a/Documentation/sp_BlitzIndex_Checks_by_Priority.md b/Documentation/sp_BlitzIndex_Checks_by_Priority.md index 4da92c722..04ecd4666 100644 --- a/Documentation/sp_BlitzIndex_Checks_by_Priority.md +++ b/Documentation/sp_BlitzIndex_Checks_by_Priority.md @@ -6,71 +6,72 @@ Before adding a new check, make sure to add a Github issue for it first, and hav If you want to change anything about a check - the priority, finding, URL, or ID - open a Github issue first. The relevant scripts have to be updated too. -CURRENT HIGH CHECKID: 126 -If you want to add a new check, start at 127. +CURRENT HIGH CHECKID: 127 +If you want to add a new check, start at 128. -| Priority | FindingsGroup | Finding | URL | CheckID | -| -------- | ----------------------- | --------------------------------------------------------------- | ---------------------------------------------------------------- | ------- | -| 10 | Over-Indexing | Many NC Indexes on a Single Table | https://www.brentozar.com/go/IndexHoarder | 20 | -| 10 | Over-Indexing | Unused NC Index with High Writes | https://www.brentozar.com/go/IndexHoarder | 22 | -| 10 | Resumable Indexing | Resumable Index Operation Paused | https://www.BrentOzar.com/go/resumable | 122 | -| 10 | Resumable Indexing | Resumable Index Operation Running | https://www.BrentOzar.com/go/resumable | 123 | -| 20 | Redundant Indexes | Duplicate Keys | https://www.brentozar.com/go/duplicateindex | 1 | -| 30 | Redundant Indexes | Approximate Duplicate Keys | https://www.brentozar.com/go/duplicateindex | 2 | -| 40 | Index Suggestion | High Value Missing Index | https://www.brentozar.com/go/indexaphobia | 50 | -| 70 | Locking-Prone Indexes | Total Lock Time with Long Average Waits | https://www.brentozar.com/go/aggressiveindexes | 11 | -| 70 | Locking-Prone Indexes | Total Lock Time with Short Average Waits | https://www.brentozar.com/go/aggressiveindexes | 12 | -| 80 | Abnormal Design Pattern | Columnstore Indexes with Trace Flag 834 | https://support.microsoft.com/en-us/kb/3210239 | 72 | -| 80 | Abnormal Design Pattern | Identity Column Near End of Range | https://www.brentozar.com/go/AbnormalPsychology | 68 | -| 80 | Abnormal Design Pattern | Filter Columns Not In Index Definition | https://www.brentozar.com/go/IndexFeatures | 34 | -| 80 | Abnormal Design Pattern | History Table With NonClustered Index | https://sqlserverfast.com/blog/hugo/2023/09/an-update-on-merge/ | 124 | -| 90 | Statistics Warnings | Low Sampling Rates | https://www.brentozar.com/go/stats | 91 | -| 90 | Statistics Warnings | Persisted Sampling Rates (Unexpected) | `https://www.youtube.com/watch?v=V5illj_KOJg&t=758s` | 125 | -| 90 | Statistics Warnings | Statistics Not Updated Recently | https://www.brentozar.com/go/stats | 90 | -| 90 | Statistics Warnings | Statistics with NO RECOMPUTE | https://www.brentozar.com/go/stats | 92 | -| 100 | Over-Indexing | NC index with High Writes:Reads | https://www.brentozar.com/go/IndexHoarder | 48 | -| 100 | Indexes Worth Reviewing | Heap with a Nonclustered Primary Key | https://www.brentozar.com/go/SelfLoathing | 47 | -| 100 | Indexes Worth Reviewing | Heap with Forwarded Fetches | https://www.brentozar.com/go/SelfLoathing | 43 | -| 100 | Indexes Worth Reviewing | Large Active Heap | https://www.brentozar.com/go/SelfLoathing | 44 | -| 100 | Indexes Worth Reviewing | Low Fill Factor on Clustered Index | https://www.brentozar.com/go/SelfLoathing | 40 | -| 100 | Indexes Worth Reviewing | Low Fill Factor on Nonclustered Index | https://www.brentozar.com/go/SelfLoathing | 40 | -| 100 | Indexes Worth Reviewing | Medium Active Heap | https://www.brentozar.com/go/SelfLoathing | 45 | -| 100 | Indexes Worth Reviewing | Small Active Heap | https://www.brentozar.com/go/SelfLoathing | 46 | -| 100 | Forced Serialization | Computed Column with Scalar UDF | https://www.brentozar.com/go/serialudf | 99 | -| 100 | Forced Serialization | Check Constraint with Scalar UDF | https://www.brentozar.com/go/computedscalar | 94 | -| 150 | Abnormal Design Pattern | Cascading Updates or Deletes | https://www.brentozar.com/go/AbnormalPsychology | 71 | -| 150 | Abnormal Design Pattern | Unindexed Foreign Keys | https://www.brentozar.com/go/AbnormalPsychology | 72 | -| 150 | Abnormal Design Pattern | Columnstore Index | https://www.brentozar.com/go/AbnormalPsychology | 61 | -| 150 | Abnormal Design Pattern | Column Collation Does Not Match Database Collation | https://www.brentozar.com/go/AbnormalPsychology | 69 | -| 150 | Abnormal Design Pattern | Compressed Index | https://www.brentozar.com/go/AbnormalPsychology | 63 | -| 150 | Abnormal Design Pattern | In-Memory OLTP | https://www.brentozar.com/go/AbnormalPsychology | 73 | -| 150 | Abnormal Design Pattern | Non-Aligned Index on a Partitioned Table | https://www.brentozar.com/go/AbnormalPsychology | 65 | -| 150 | Abnormal Design Pattern | Partitioned Index | https://www.brentozar.com/go/AbnormalPsychology | 64 | -| 150 | Abnormal Design Pattern | Spatial Index | https://www.brentozar.com/go/AbnormalPsychology | 62 | -| 150 | Abnormal Design Pattern | XML Index | https://www.brentozar.com/go/AbnormalPsychology | 60 | -| 150 | Over-Indexing | Approximate: Wide Indexes (7 or More Columns) | https://www.brentozar.com/go/IndexHoarder | 23 | -| 150 | Over-Indexing | More Than 5 Percent NC Indexes Are Unused | https://www.brentozar.com/go/IndexHoarder | 21 | -| 150 | Over-Indexing | Non-Unique Clustered Index | https://www.brentozar.com/go/IndexHoarder | 28 | -| 150 | Over-Indexing | Unused NC Index with Low Writes | https://www.brentozar.com/go/IndexHoarder | 29 | -| 150 | Over-Indexing | Wide Clustered Index (>3 columns or >16 bytes) | https://www.brentozar.com/go/IndexHoarder | 24 | -| 150 | Indexes Worth Reviewing | Disabled Index | https://www.brentozar.com/go/SelfLoathing | 42 | -| 150 | Indexes Worth Reviewing | Hypothetical Index | https://www.brentozar.com/go/SelfLoathing | 41 | -| 200 | Abnormal Design Pattern | Identity Column Using a Negative Seed or Increment Other Than 1 | https://www.brentozar.com/go/AbnormalPsychology | 74 | -| 200 | Abnormal Design Pattern | Recently Created Tables/Indexes (1 week) | https://www.brentozar.com/go/AbnormalPsychology | 66 | -| 200 | Abnormal Design Pattern | Recently Modified Tables/Indexes (2 days) | https://www.brentozar.com/go/AbnormalPsychology | 67 | -| 200 | Abnormal Design Pattern | Replicated Columns | https://www.brentozar.com/go/AbnormalPsychology | 70 | -| 200 | Abnormal Design Pattern | Temporal Tables | https://www.brentozar.com/go/AbnormalPsychology | 110 | -| 200 | Repeated Calculations | Computed Columns Not Persisted | https://www.brentozar.com/go/serialudf | 100 | -| 200 | Statistics Warnings | Statistics With Filters | https://www.brentozar.com/go/stats | 93 | -| 200 | Statistics Warnings | Persisted Sampling Rates (Expected) | `https://www.youtube.com/watch?v=V5illj_KOJg&t=758s` | 126 | -| 200 | Over-Indexing | High Ratio of Nulls | https://www.brentozar.com/go/IndexHoarder | 25 | -| 200 | Over-Indexing | High Ratio of Strings | https://www.brentozar.com/go/IndexHoarder | 27 | -| 200 | Over-Indexing | Wide Tables: 35+ cols or > 2000 non-LOB bytes | https://www.brentozar.com/go/IndexHoarder | 26 | -| 200 | Indexes Worth Reviewing | Heaps with Deletes | https://www.brentozar.com/go/SelfLoathing | 49 | -| 200 | High Workloads | Scan-a-lots (index-usage-stats) | https://www.brentozar.com/go/Workaholics | 80 | -| 200 | High Workloads | Top Recent Accesses (index-op-stats) | https://www.brentozar.com/go/Workaholics | 81 | -| 250 | Omitted Index Features | Few Indexes Use Includes | https://www.brentozar.com/go/IndexFeatures | 31 | -| 250 | Omitted Index Features | No Filtered Indexes or Indexed Views | https://www.brentozar.com/go/IndexFeatures | 32 | -| 250 | Omitted Index Features | No Indexes Use Includes | https://www.brentozar.com/go/IndexFeatures | 30 | -| 250 | Omitted Index Features | Potential Filtered Index (Based on Column Name) | https://www.brentozar.com/go/IndexFeatures | 33 | -| 250 | Specialized Indexes | Optimized For Sequential Keys | | 121 | +| Priority | FindingsGroup | Finding | URL | CheckID | +| -------- | ----------------------- | --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ------- | +| 10 | Over-Indexing | Many NC Indexes on a Single Table | https://www.brentozar.com/go/IndexHoarder | 20 | +| 10 | Over-Indexing | Unused NC Index with High Writes | https://www.brentozar.com/go/IndexHoarder | 22 | +| 10 | Resumable Indexing | Resumable Index Operation Paused | https://www.BrentOzar.com/go/resumable | 122 | +| 10 | Resumable Indexing | Resumable Index Operation Running | https://www.BrentOzar.com/go/resumable | 123 | +| 20 | Redundant Indexes | Duplicate Keys | https://www.brentozar.com/go/duplicateindex | 1 | +| 30 | Redundant Indexes | Approximate Duplicate Keys | https://www.brentozar.com/go/duplicateindex | 2 | +| 40 | Index Suggestion | High Value Missing Index | https://www.brentozar.com/go/indexaphobia | 50 | +| 70 | Locking-Prone Indexes | Total Lock Time with Long Average Waits | https://www.brentozar.com/go/aggressiveindexes | 11 | +| 70 | Locking-Prone Indexes | Total Lock Time with Short Average Waits | https://www.brentozar.com/go/aggressiveindexes | 12 | +| 80 | Abnormal Design Pattern | Columnstore Indexes with Trace Flag 834 | https://support.microsoft.com/en-us/kb/3210239 | 72 | +| 80 | Abnormal Design Pattern | Identity Column Near End of Range | https://www.brentozar.com/go/AbnormalPsychology | 68 | +| 80 | Abnormal Design Pattern | Filter Columns Not In Index Definition | https://www.brentozar.com/go/IndexFeatures | 34 | +| 80 | Abnormal Design Pattern | History Table With NonClustered Index | https://sqlserverfast.com/blog/hugo/2023/09/an-update-on-merge/ | 124 | +| 90 | Statistics Warnings | Low Sampling Rates | https://www.brentozar.com/go/stats | 91 | +| 90 | Statistics Warnings | Persisted Sampling Rates (Unexpected) | `https://www.youtube.com/watch?v=V5illj_KOJg&t=758s` | 125 | +| 90 | Statistics Warnings | Statistics Not Updated Recently | https://www.brentozar.com/go/stats | 90 | +| 90 | Statistics Warnings | Statistics with NO RECOMPUTE | https://www.brentozar.com/go/stats | 92 | +| 100 | Over-Indexing | NC index with High Writes:Reads | https://www.brentozar.com/go/IndexHoarder | 48 | +| 100 | Indexes Worth Reviewing | Heap with a Nonclustered Primary Key | https://www.brentozar.com/go/SelfLoathing | 47 | +| 100 | Indexes Worth Reviewing | Heap with Forwarded Fetches | https://www.brentozar.com/go/SelfLoathing | 43 | +| 100 | Indexes Worth Reviewing | Large Active Heap | https://www.brentozar.com/go/SelfLoathing | 44 | +| 100 | Indexes Worth Reviewing | Low Fill Factor on Clustered Index | https://www.brentozar.com/go/SelfLoathing | 40 | +| 100 | Indexes Worth Reviewing | Low Fill Factor on Nonclustered Index | https://www.brentozar.com/go/SelfLoathing | 40 | +| 100 | Indexes Worth Reviewing | Medium Active Heap | https://www.brentozar.com/go/SelfLoathing | 45 | +| 100 | Indexes Worth Reviewing | Small Active Heap | https://www.brentozar.com/go/SelfLoathing | 46 | +| 100 | Forced Serialization | Computed Column with Scalar UDF | https://www.brentozar.com/go/serialudf | 99 | +| 100 | Forced Serialization | Check Constraint with Scalar UDF | https://www.brentozar.com/go/computedscalar | 94 | +| 150 | Abnormal Design Pattern | Cascading Updates or Deletes | https://www.brentozar.com/go/AbnormalPsychology | 71 | +| 150 | Abnormal Design Pattern | Unindexed Foreign Keys | https://www.brentozar.com/go/AbnormalPsychology | 72 | +| 150 | Abnormal Design Pattern | Columnstore Index | https://www.brentozar.com/go/AbnormalPsychology | 61 | +| 150 | Abnormal Design Pattern | Column Collation Does Not Match Database Collation | https://www.brentozar.com/go/AbnormalPsychology | 69 | +| 150 | Abnormal Design Pattern | Compressed Index | https://www.brentozar.com/go/AbnormalPsychology | 63 | +| 150 | Abnormal Design Pattern | In-Memory OLTP | https://www.brentozar.com/go/AbnormalPsychology | 73 | +| 150 | Abnormal Design Pattern | Non-Aligned Index on a Partitioned Table | https://www.brentozar.com/go/AbnormalPsychology | 65 | +| 150 | Abnormal Design Pattern | Partitioned Index | https://www.brentozar.com/go/AbnormalPsychology | 64 | +| 150 | Abnormal Design Pattern | Spatial Index | https://www.brentozar.com/go/AbnormalPsychology | 62 | +| 150 | Abnormal Design Pattern | XML Index | https://www.brentozar.com/go/AbnormalPsychology | 60 | +| 150 | Over-Indexing | Approximate: Wide Indexes (7 or More Columns) | https://www.brentozar.com/go/IndexHoarder | 23 | +| 150 | Over-Indexing | More Than 5 Percent NC Indexes Are Unused | https://www.brentozar.com/go/IndexHoarder | 21 | +| 150 | Over-Indexing | Non-Unique Clustered Index | https://www.brentozar.com/go/IndexHoarder | 28 | +| 150 | Over-Indexing | Unused NC Index with Low Writes | https://www.brentozar.com/go/IndexHoarder | 29 | +| 150 | Over-Indexing | Wide Clustered Index (>3 columns or >16 bytes) | https://www.brentozar.com/go/IndexHoarder | 24 | +| 150 | Indexes Worth Reviewing | Disabled Index | https://www.brentozar.com/go/SelfLoathing | 42 | +| 150 | Indexes Worth Reviewing | Hypothetical Index | https://www.brentozar.com/go/SelfLoathing | 41 | +| 200 | Abnormal Design Pattern | Identity Column Using a Negative Seed or Increment Other Than 1 | https://www.brentozar.com/go/AbnormalPsychology | 74 | +| 200 | Abnormal Design Pattern | Recently Created Tables/Indexes (1 week) | https://www.brentozar.com/go/AbnormalPsychology | 66 | +| 200 | Abnormal Design Pattern | Recently Modified Tables/Indexes (2 days) | https://www.brentozar.com/go/AbnormalPsychology | 67 | +| 200 | Abnormal Design Pattern | Replicated Columns | https://www.brentozar.com/go/AbnormalPsychology | 70 | +| 200 | Abnormal Design Pattern | Temporal Tables | https://www.brentozar.com/go/AbnormalPsychology | 110 | +| 200 | Repeated Calculations | Computed Columns Not Persisted | https://www.brentozar.com/go/serialudf | 100 | +| 200 | Statistics Warnings | Statistics With Filters | https://www.brentozar.com/go/stats | 93 | +| 200 | Statistics Warnings | Persisted Sampling Rates (Expected) | `https://www.youtube.com/watch?v=V5illj_KOJg&t=758s` | 126 | +| 200 | Statistics Warnings | Partitioned Table Without Incremental Statistics | https://sqlperformance.com/2015/05/sql-statistics/improving-maintenance-incremental-statistics | 127 | +| 200 | Over-Indexing | High Ratio of Nulls | https://www.brentozar.com/go/IndexHoarder | 25 | +| 200 | Over-Indexing | High Ratio of Strings | https://www.brentozar.com/go/IndexHoarder | 27 | +| 200 | Over-Indexing | Wide Tables: 35+ cols or > 2000 non-LOB bytes | https://www.brentozar.com/go/IndexHoarder | 26 | +| 200 | Indexes Worth Reviewing | Heaps with Deletes | https://www.brentozar.com/go/SelfLoathing | 49 | +| 200 | High Workloads | Scan-a-lots (index-usage-stats) | https://www.brentozar.com/go/Workaholics | 80 | +| 200 | High Workloads | Top Recent Accesses (index-op-stats) | https://www.brentozar.com/go/Workaholics | 81 | +| 250 | Omitted Index Features | Few Indexes Use Includes | https://www.brentozar.com/go/IndexFeatures | 31 | +| 250 | Omitted Index Features | No Filtered Indexes or Indexed Views | https://www.brentozar.com/go/IndexFeatures | 32 | +| 250 | Omitted Index Features | No Indexes Use Includes | https://www.brentozar.com/go/IndexFeatures | 30 | +| 250 | Omitted Index Features | Potential Filtered Index (Based on Column Name) | https://www.brentozar.com/go/IndexFeatures | 33 | +| 250 | Specialized Indexes | Optimized For Sequential Keys | | 121 | diff --git a/sp_BlitzIndex.sql b/sp_BlitzIndex.sql index feb81a434..078d5c5df 100644 --- a/sp_BlitzIndex.sql +++ b/sp_BlitzIndex.sql @@ -726,6 +726,7 @@ IF OBJECT_ID('tempdb..#dm_db_index_operational_stats') IS NOT NULL CREATE TABLE #Statistics ( database_id INT NOT NULL, database_name NVARCHAR(256) NOT NULL, + object_id INT NOT NULL, table_name NVARCHAR(128) NULL, schema_name NVARCHAR(128) NULL, index_name NVARCHAR(128) NULL, @@ -746,7 +747,8 @@ IF OBJECT_ID('tempdb..#dm_db_index_operational_stats') IS NOT NULL no_recompute BIT NULL, has_filter BIT NULL, filter_definition NVARCHAR(MAX) NULL, - persisted_sample_percent FLOAT NULL + persisted_sample_percent FLOAT NULL, + is_incremental BIT NULL ); CREATE TABLE #ComputedColumns @@ -2284,14 +2286,15 @@ OPTION (RECOMPILE);'; BEGIN RAISERROR (N'Gathering Statistics Info With Newer Syntax.',0,1) WITH NOWAIT; SET @dsql=N'USE ' + QUOTENAME(@DatabaseName) + N'; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; - INSERT #Statistics ( database_id, database_name, table_name, schema_name, index_name, column_names, statistics_name, last_statistics_update, + INSERT #Statistics ( database_id, database_name, object_id, table_name, schema_name, index_name, column_names, statistics_name, last_statistics_update, days_since_last_stats_update, rows, rows_sampled, percent_sampled, histogram_steps, modification_counter, percent_modifications, modifications_before_auto_update, index_type_desc, table_create_date, table_modify_date, - no_recompute, has_filter, filter_definition, persisted_sample_percent) + no_recompute, has_filter, filter_definition, persisted_sample_percent, is_incremental) SELECT DB_ID(N' + QUOTENAME(@DatabaseName,'''') + N') AS [database_id], - @i_DatabaseName AS database_name, - obj.name AS table_name, - sch.name AS schema_name, + @i_DatabaseName AS database_name, + obj.object_id, + obj.name AS table_name, + sch.name AS schema_name, ISNULL(i.name, ''System Or User Statistic'') AS index_name, ca.column_names AS column_names, s.name AS statistics_name, @@ -2323,8 +2326,16 @@ OPTION (RECOMPILE);'; FROM sys.all_columns AS all_cols WHERE all_cols.[object_id] = OBJECT_ID(N'sys.dm_db_stats_properties', N'IF') AND all_cols.[name] = N'persisted_sample_percent' ) - THEN N'ddsp.persisted_sample_percent' - ELSE N'NULL AS persisted_sample_percent' END + THEN N'ddsp.persisted_sample_percent,' + ELSE N'NULL AS persisted_sample_percent,' END + + CASE WHEN EXISTS + ( + SELECT 1 + FROM sys.all_columns AS all_cols + WHERE all_cols.[object_id] = OBJECT_ID(N'sys.stats', N'V') AND all_cols.[name] = N'is_incremental' + ) + THEN N's.is_incremental' + ELSE N'NULL AS is_incremental' END + N' FROM ' + QUOTENAME(@DatabaseName) + N'.sys.stats AS s JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.objects obj @@ -2370,12 +2381,13 @@ OPTION (RECOMPILE);'; BEGIN RAISERROR (N'Gathering Statistics Info With Older Syntax.',0,1) WITH NOWAIT; SET @dsql=N'USE ' + QUOTENAME(@DatabaseName) + N'; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; - INSERT #Statistics(database_id, database_name, table_name, schema_name, index_name, column_names, statistics_name, + INSERT #Statistics(database_id, database_name, object_id, table_name, schema_name, index_name, column_names, statistics_name, last_statistics_update, days_since_last_stats_update, rows, modification_counter, percent_modifications, modifications_before_auto_update, index_type_desc, table_create_date, table_modify_date, - no_recompute, has_filter, filter_definition, persisted_sample_percent) + no_recompute, has_filter, filter_definition, persisted_sample_percent, is_incremental) SELECT DB_ID(N' + QUOTENAME(@DatabaseName,'''') + N') AS [database_id], @i_DatabaseName AS database_name, + obj.object_id, obj.name AS table_name, sch.name AS schema_name, ISNULL(i.name, ''System Or User Statistic'') AS index_name, @@ -2402,7 +2414,16 @@ OPTION (RECOMPILE);'; ELSE N'NULL AS has_filter, NULL AS filter_definition,' END /* Certainly NULL. This branch does not even join on the table that this column comes from. */ - + N'NULL AS persisted_sample_percent' + + N'NULL AS persisted_sample_percent, + ' + + CASE WHEN EXISTS + ( + SELECT 1 + FROM sys.all_columns AS all_cols + WHERE all_cols.[object_id] = OBJECT_ID(N'sys.stats', N'V') AND all_cols.[name] = N'is_incremental' + ) + THEN N's.is_incremental' + ELSE N'NULL AS is_incremental' END + N' FROM ' + QUOTENAME(@DatabaseName) + N'.sys.stats AS s INNER HASH JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.sysindexes si @@ -4475,7 +4496,7 @@ BEGIN END; ---------------------------------------- - --Statistics Info: Check_id 90-99, as well as 125-126 + --Statistics Info: Check_id 90-99, as well as 125 ---------------------------------------- RAISERROR(N'check_id 90: Outdated statistics', 0,1) WITH NOWAIT; @@ -5651,7 +5672,7 @@ BEGIN OPTION ( RECOMPILE ); - + /* See check_id 125. */ RAISERROR(N'check_id 126: Persisted Sampling Rates (Expected)', 0,1) WITH NOWAIT; INSERT #BlitzIndexResults ( check_id, Priority, findings_group, finding, [database_name], URL, details, index_definition, secret_columns, index_usage_summary, index_size_summary ) @@ -5672,7 +5693,49 @@ BEGIN GROUP BY s.database_name OPTION ( RECOMPILE ); - + RAISERROR(N'check_id 127: Partitioned Table Without Incremental Statistics', 0,1) WITH NOWAIT; + INSERT #BlitzIndexResults ( check_id, Priority, findings_group, finding, [database_name], URL, details, index_definition, + secret_columns, index_usage_summary, index_size_summary, more_info ) + SELECT 127 AS check_id, + 200 AS Priority, + 'Statistics Warnings' AS findings_group, + 'Partitioned Table Without Incremental Statistics', + partitioned_tables.database_name, + 'https://sqlperformance.com/2015/05/sql-statistics/improving-maintenance-incremental-statistics' AS URL, + 'The table ' + QUOTENAME(partitioned_tables.schema_name) + '.' + QUOTENAME(partitioned_tables.object_name) + ' is partitioned, but ' + + CONVERT(NVARCHAR(100), incremental_stats_counts.not_incremental_stats_count) + ' of its ' + CONVERT(NVARCHAR(100), incremental_stats_counts.stats_count) + + ' statistics are not incremental. If this is a sliding/rolling window table, then consider making the statistics incremental. If not, then investigate why this table is partitioned.' AS details, + partitioned_tables.object_name + N' (Entire table)' AS index_definition, + 'N/A' AS secret_columns, + 'N/A' AS index_usage_summary, + 'N/A' AS index_size_summary, + partitioned_tables.more_info + FROM + ( + SELECT s.database_id, + s.object_id, + COUNT(CASE WHEN s.is_incremental = 0 THEN 1 END) AS not_incremental_stats_count, + COUNT(*) AS stats_count + FROM #Statistics AS s + GROUP BY s.database_id, s.object_id + HAVING COUNT(CASE WHEN s.is_incremental = 0 THEN 1 END) > 0 + ) AS incremental_stats_counts + JOIN + ( + /* Just get the tables. We do not need the indexes. */ + SELECT DISTINCT i.database_name, + i.database_id, + i.object_id, + i.schema_name, + i.object_name, + /* This is a little bit dishonest, since it tells us nothing about if the statistics are incremental. */ + i.more_info + FROM #IndexSanity AS i + WHERE i.partition_key_column_name IS NOT NULL + ) AS partitioned_tables + ON partitioned_tables.database_id = incremental_stats_counts.database_id AND partitioned_tables.object_id = incremental_stats_counts.object_id + /* No need for a GROUP BY. What we are joining on has exactly one row in each sub-query. */ + OPTION ( RECOMPILE ); END /* IF @Mode = 4 */