diff --git a/README.md b/README.md index e4f6bf5b..f9778e90 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,7 @@ Current valid parameter details: | @log_schema_name | sysname | schema to store logging tables | valid schema name | NULL | | @log_table_name_prefix | sysname | prefix for all logging tables | valid table name prefix | 'PressureDetector' | | @log_retention_days | integer | Number of days to keep logs, 0 = keep indefinitely | integer | 30 | +| @troubleshoot_blocking | bit | show blocking chains instead of pressure analysis | 0 or 1 | 0 | | @help | bit | how you got here | 0 or 1 | 0 | | @debug | bit | prints dynamic sql, displays parameter and variable values, and table contents | 0 or 1 | 0 | | @version | varchar | OUTPUT; for support | none | none; OUTPUT | @@ -125,7 +126,8 @@ Current valid parameter details: | @object_schema | sysname | (inclusive) the schema of the object you want to filter to; only needed with blocking events | a stringy thing | dbo | | @requested_memory_mb | integer | (>=) the memory grant a query must ask for to have data collected | an integer | 0 | | @seconds_sample | tinyint | the duration in seconds to run the event session for | an integer | 10 | -| @gimme_danger | bit | used to override default minimums for query, wait, and blocking durations. | 1 or 0 | 0 | +| @gimme_danger | bit | used to override default duration minimums for wait events, including zero-duration waits | 1 or 0 | 0 | +| @target_output | sysname | output target for extended events | "ring_buffer" or "event_file" (event_file not available for Azure SQL DB or Managed Instance) | "ring_buffer" | | @keep_alive | bit | creates a permanent session, either to watch live or log to a table from | 1 or 0 | 0 | | @custom_name | nvarchar | if you want to custom name a permanent session | a stringy thing | intentionally left blank | | @output_database_name | sysname | the database you want to log data to | a valid database name | intentionally left blank | @@ -217,6 +219,7 @@ Current valid parameter details: | @log_schema_name | sysname | schema to store logging tables | a valid schema name | NULL | | @log_table_name_prefix| sysname | prefix for all logging tables | a valid table name prefix | 'HumanEventsBlockViewer' | | @log_retention_days | integer | Number of days to keep logs, 0 = keep indefinitely | a valid integer | 30 | +| @max_blocking_events | integer | maximum blocking events to analyze, 0 = unlimited | 0 to 2147483647 | 5000 | | @help | bit | how you got here | 0 or 1 | 0 | | @debug | bit | dumps raw temp table contents | 0 or 1 | 0 | | @version | varchar | OUTPUT; for support | none; OUTPUT | none; OUTPUT | @@ -259,7 +262,7 @@ Current valid parameter details: | parameter_name | data_type | description | valid_inputs | defaults | |-----------------------------------------|----------------|---------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------| | @database_name | sysname | the name of the database you want to look at query store in | a database name with query store enabled | NULL; current database name if NULL | -| @sort_order | varchar | the runtime metric you want to prioritize results by | cpu, logical reads, physical reads, writes, duration, memory, tempdb, executions, recent, plan count by hashes, cpu waits, lock waits, locks waits, latch waits, latches waits, buffer latch waits, buffer latches waits, buffer io waits, log waits, log io waits, network waits, network io waits, parallel waits, parallelism waits, memory waits, total waits, rows | cpu | +| @sort_order | varchar | the runtime metric you want to prioritize results by | cpu, logical reads, physical reads, writes, duration, memory, tempdb, executions, recent, plan count by hashes, cpu waits, lock waits, locks waits, latch waits, latches waits, buffer latch waits, buffer latches waits, buffer io waits, log waits, log io waits, network waits, network io waits, parallel waits, parallelism waits, memory waits, total waits, rows, total cpu, total logical reads, total physical reads, total writes, total duration, total memory, total tempdb, total rows (avg prefix also accepted, e.g. "avg cpu") | cpu | | @top | bigint | the number of queries you want to pull back | a positive integer between 1 and 9,223,372,036,854,775,807 | 10 | | @start_date | datetimeoffset | the begin date of your search, will be converted to UTC internally | January 1, 1753, through December 31, 9999 | the last seven days | | @end_date | datetimeoffset | the end date of your search, will be converted to UTC internally | January 1, 1753, through December 31, 9999 | NULL | @@ -294,8 +297,8 @@ Current valid parameter details: | @hide_help_table | bit | hides the "bottom table" that shows help and support information | 0 or 1 | 0 | | @format_output | bit | returns numbers formatted with commas | 0 or 1 | 1 | | @get_all_databases | bit | looks for query store enabled user databases and returns combined results from all of them | 0 or 1 | 0 | -| @include_databases | nvarchar(4000) | comma-separated list of databases to include (only when @get_all_databases = 1) | a string; comma separated database names | NULL | -| @exclude_databases | nvarchar(4000) | comma-separated list of databases to exclude (only when @get_all_databases = 1) | a string; comma separated database names | NULL | +| @include_databases | nvarchar(MAX) | comma-separated list of databases to include (only when @get_all_databases = 1) | a string; comma separated database names | NULL | +| @exclude_databases | nvarchar(MAX) | comma-separated list of databases to exclude (only when @get_all_databases = 1) | a string; comma separated database names | NULL | | @workdays | bit | use this to filter out weekends and after-hours queries | 0 or 1 | 0 | | @work_start | time | use this to set a specific start of your work days | a time like 8am, 9am or something | 9am | | @work_end | time | use this to set a specific end of your work days | a time like 5pm, 6pm or something | 5pm | @@ -304,6 +307,7 @@ Current valid parameter details: | @regression_comparator | varchar | what difference to use ('relative' or 'absolute') when comparing @sort_order's metric for the normal time period with any regression time period. | relative, absolute | NULL; absolute if @regression_baseline_start_date is specified | | @regression_direction | varchar | when comparing against any regression baseline, what do you want the results sorted by ('magnitude', 'improved', or 'regressed')? | regressed, worse, improved, better, magnitude, absolute, whatever | NULL; regressed if @regression_baseline_start_date is specified | | @include_query_hash_totals | bit | will add an additional column to final output with total resource usage by query hash | 0 or 1 | 0 | +| @include_maintenance | bit | add maintenance operations such as index creation to the result set | 0 or 1 | 0 | | @help | bit | how you got here | 0 or 1 | 0 | | @debug | bit | prints dynamic sql, statement length, parameter and variable values, and raw temp table contents | 0 or 1 | 0 | | @troubleshoot_performance | bit | set statistics xml on for queries against views | 0 or 1 | 0 | @@ -337,15 +341,21 @@ Current valid parameter details: | parameter_name | data_type | description | valid_inputs | defaults | |---------------------|------------|-----------------------------------------------------------------------|----------------------------------------------------|----------------------------------| -| @database_name | sysname | the name of the database you want to extract queries from | a database name with query store enabled | NULL; current database if NULL | -| @start_date | datetime2 | the begin date of your search | January 1, 1753, through December 31, 9999 | the last seven days | -| @end_date | datetime2 | the end date of your search | January 1, 1753, through December 31, 9999 | current date/time | -| @include_plan_ids | nvarchar | a list of plan ids to search for | a string; comma separated for multiple ids | NULL | -| @include_query_ids | nvarchar | a list of query ids to search for | a string; comma separated for multiple ids | NULL | -| @help | bit | how you got here | 0 or 1 | 0 | -| @debug | bit | prints dynamic sql and statement length | 0 or 1 | 0 | -| @version | varchar | OUTPUT; for support | none; OUTPUT | none; OUTPUT | -| @version_date | datetime | OUTPUT; for support | none; OUTPUT | none; OUTPUT | +| @database_name | sysname | the name of the database you want to look at query store in | a database name with query store enabled | NULL; current database if NULL | +| @start_date | datetimeoffset(7) | the begin date of your search, will be converted to UTC internally | January 1, 1753, through December 31, 9999 | the last seven days | +| @end_date | datetimeoffset(7) | the end date of your search, will be converted to UTC internally | January 1, 1753, through December 31, 9999 | current date/time | +| @include_plan_ids | nvarchar(4000) | a list of plan ids to search for | a string; comma separated for multiple ids | NULL | +| @include_query_ids | nvarchar(4000) | a list of query ids to search for | a string; comma separated for multiple ids | NULL | +| @ignore_plan_ids | nvarchar(4000) | a list of plan ids to ignore | a string; comma separated for multiple ids | NULL | +| @ignore_query_ids | nvarchar(4000) | a list of query ids to ignore | a string; comma separated for multiple ids | NULL | +| @procedure_schema | sysname | the schema of the procedure you're searching for | a valid schema in your database | NULL | +| @procedure_name | sysname | the name of the programmable object you're searching for | a valid programmable object in your database | NULL | +| @query_text_search | nvarchar(4000) | query text to search for | a string; leading and trailing wildcards added | NULL | +| @query_text_search_not | nvarchar(4000) | query text to exclude | a string; leading and trailing wildcards added | NULL | +| @help | bit | return available parameter details, etc. | 0 or 1 | 0 | +| @debug | bit | prints dynamic sql, statement length, parameter and variable values | 0 or 1 | 0 | +| @version | varchar(30) | OUTPUT; for support | none; OUTPUT | none; OUTPUT | +| @version_date | datetime | OUTPUT; for support | none; OUTPUT | none; OUTPUT | [*Back to top*](#navigatory) @@ -383,10 +393,12 @@ Current valid parameter details: | @end_date | datetimeoffset | latest date to show data for, will be internally converted to UTC | a reasonable date | current date | | @warnings_only | bit | only show rows where a warning was reported | NULL, 0, 1 | 0 | | @database_name | sysname | database name to show blocking events for | the name of a database | NULL | -| @wait_duration_ms | bigint | minimum wait duration | the minimum duration of a wait for queries with interesting waits | 0 | +| @wait_duration_ms | bigint | minimum wait duration | the minimum duration of a wait for queries with interesting waits | 500 | | @wait_round_interval_minutes | bigint | interval to round minutes to for wait stats | interval to round minutes to for top wait stats by count and duration | 60 | | @skip_locks | bit | skip the blocking and deadlocking section | 0 or 1 | 0 | +| @skip_waits | bit | skip the wait stats section | 0 or 1 | 0 | | @pending_task_threshold | integer | minimum number of pending tasks to display | a valid integer | 10 | +| @use_ring_buffer | bit | use ring_buffer target instead of file target for faster collection | 0 or 1 | 0 | | @log_to_table | bit | enable logging to permanent tables | 0 or 1 | 0 | | @log_database_name | sysname | database to store logging tables | valid database name | NULL | | @log_schema_name | sysname | schema to store logging tables | valid schema name | NULL | @@ -421,7 +433,7 @@ Current valid parameter details: | @days_back | integer | how many days back you want to search the logs | an integer; will be converted to a negative number automatically | -7 | | @start_date | datetime | if you want to search a specific time frame | a datetime value | NULL | | @end_date | datetime | if you want to search a specific time frame | a datetime value | NULL | -| @custom_message | nvarchar | if you want to search for a custom string | something specific you want to search for. no wildcards or substitions. | NULL | +| @custom_message | nvarchar | if you want to search for a custom string | something specific you want to search for. no wildcards or substitutions. | NULL | | @custom_message_only | bit | only search for the custom string | NULL, 0, 1 | 0 | | @first_log_only | bit | only search through the first error log | NULL, 0, 1 | 0 | | @language_id | integer | to use something other than English | SELECT DISTINCT m.language_id FROM sys.messages AS m ORDER BY m.language_id; | 1033 | diff --git a/sp_HealthParser/README.md b/sp_HealthParser/README.md index 6b65ee47..80272d8d 100644 --- a/sp_HealthParser/README.md +++ b/sp_HealthParser/README.md @@ -33,10 +33,12 @@ Typical result set will show you: | @end_date | datetimeoffset | latest date to show data for, will be internally converted to UTC | a reasonable date | current date | | @warnings_only | bit | only show rows where a warning was reported | NULL, 0, 1 | 0 | | @database_name | sysname | database name to show blocking events for | the name of a database | NULL | -| @wait_duration_ms | bigint | minimum wait duration | the minimum duration of a wait for queries with interesting waits | 0 | +| @wait_duration_ms | bigint | minimum wait duration | the minimum duration of a wait for queries with interesting waits | 500 | | @wait_round_interval_minutes | bigint | interval to round minutes to for wait stats | interval to round minutes to for top wait stats by count and duration | 60 | | @skip_locks | bit | skip the blocking and deadlocking section | 0 or 1 | 0 | +| @skip_waits | bit | skip the wait stats section | 0 or 1 | 0 | | @pending_task_threshold | integer | minimum number of pending tasks to display | a valid integer | 10 | +| @use_ring_buffer | bit | use ring_buffer target instead of file target for faster collection | 0 or 1 | 0 | | @log_to_table | bit | enable logging to permanent tables | 0 or 1 | 0 | | @log_database_name | sysname | database to store logging tables | valid database name | NULL | | @log_schema_name | sysname | schema to store logging tables | valid schema name | NULL | diff --git a/sp_HumanEvents/README.md b/sp_HumanEvents/README.md index 0e916b88..826a8f66 100644 --- a/sp_HumanEvents/README.md +++ b/sp_HumanEvents/README.md @@ -63,7 +63,8 @@ Misuse of this procedure can harm performance. Be very careful about introducing | @object_schema | sysname | (inclusive) the schema of the object you want to filter to; only needed with blocking events | a stringy thing | dbo | | @requested_memory_mb | integer | (>=) the memory grant a query must ask for to have data collected | an integer | 0 | | @seconds_sample | tinyint | the duration in seconds to run the event session for | an integer | 10 | -| @gimme_danger | bit | used to override default minimums for query, wait, and blocking durations. | 1 or 0 | 0 | +| @gimme_danger | bit | used to override default duration minimums for wait events, including zero-duration waits | 1 or 0 | 0 | +| @target_output | sysname | output target for extended events | "ring_buffer" or "event_file" (event_file not available for Azure SQL DB or Managed Instance) | "ring_buffer" | | @keep_alive | bit | creates a permanent session, either to watch live or log to a table from | 1 or 0 | 0 | | @custom_name | sysname | if you want to custom name a permanent session | a stringy thing | intentionally left blank | | @output_database_name | sysname | the database you want to log data to | a valid database name | intentionally left blank | @@ -192,7 +193,7 @@ ON SERVER | @log_schema_name | sysname | schema to store logging tables | a valid schema name | NULL | | @log_table_name_prefix| sysname | prefix for all logging tables | a valid table name prefix | 'HumanEventsBlockViewer' | | @log_retention_days | integer | Number of days to keep logs, 0 = keep indefinitely | a valid integer | 30 | -| @max_blocking_events | bigint | maximum blocking events to analyze, 0 = unlimited | 0 to 9223372036854775807 | 5000 | +| @max_blocking_events | integer | maximum blocking events to analyze, 0 = unlimited | 0 to 2147483647 | 5000 | | @help | bit | how you got here | 0 or 1 | 0 | | @debug | bit | dumps raw temp table contents | 0 or 1 | 0 | | @version | varchar | OUTPUT; for support | none; OUTPUT | none; OUTPUT | diff --git a/sp_IndexCleanup/README.md b/sp_IndexCleanup/README.md index ae21e537..bc63ae0c 100644 --- a/sp_IndexCleanup/README.md +++ b/sp_IndexCleanup/README.md @@ -98,5 +98,5 @@ EXECUTE dbo.sp_IndexCleanup - When using @get_all_databases, results for all databases are combined in a single result set - The index_count column for the SUMMARY row in the output table will likely indicate a lower number than is shown at the DATABASE level. The SUMMARY level only includes indexes that have been analyzed; excluding things like clustered indexes, heaps, xml indexes, etc. The DATABASE level index_count value is the total number of indexes in the database. -Copyright 2024 Darling Data, LLC +Copyright 2026 Darling Data, LLC Released under MIT license \ No newline at end of file diff --git a/sp_LogHunter/README.md b/sp_LogHunter/README.md index 9fde86cd..5c3a1c1e 100644 --- a/sp_LogHunter/README.md +++ b/sp_LogHunter/README.md @@ -19,7 +19,7 @@ It helps you give you a fuller, better picture of any bad stuff happening. | @days_back | integer | how many days back you want to search the logs | an integer; will be converted to a negative number automatically | -7 | | @start_date | datetime | if you want to search a specific time frame | a datetime value | NULL | | @end_date | datetime | if you want to search a specific time frame | a datetime value | NULL | -| @custom_message | nvarchar | if you want to search for a custom string | something specific you want to search for. no wildcards or substitions. | NULL | +| @custom_message | nvarchar | if you want to search for a custom string | something specific you want to search for. no wildcards or substitutions. | NULL | | @custom_message_only | bit | only search for the custom string | NULL, 0, 1 | 0 | | @first_log_only | bit | only search through the first error log | NULL, 0, 1 | 0 | | @language_id | integer | to use something other than English | SELECT DISTINCT m.language_id FROM sys.messages AS m ORDER BY m.language_id; | 1033 | diff --git a/sp_PerfCheck/README.md b/sp_PerfCheck/README.md index 3a34b06c..dfa77cd8 100644 --- a/sp_PerfCheck/README.md +++ b/sp_PerfCheck/README.md @@ -1,8 +1,6 @@ # sp_PerfCheck: SQL Server Performance Health Check -`sp_PerfCheck` is a comprehensive SQL Server performance diagnostic tool that quickly identifies configuration issues, capacity problems, and performance bottlenecks. - -This procedure collects key health metrics and configuration settings at both the server and database level, providing actionable insights without overwhelming you with irrelevant information. +`sp_PerfCheck` is a comprehensive SQL Server performance diagnostic tool that quickly identifies configuration issues, capacity problems, and performance bottlenecks at both server and database levels. ## Features @@ -24,229 +22,157 @@ This procedure collects key health metrics and configuration settings at both th | Parameter | Data Type | Default | Description | |-----------|-----------|---------|-------------| -| @database_name | sysname | NULL | Specific database to check; NULL runs against all accessible user databases | +| @database_name | sysname | NULL | Specific database to check; NULL checks all accessible user databases | | @debug | bit | 0 | Print diagnostic messages and intermediate query results | | @version | varchar(30) | NULL OUTPUT | Returns version number | | @version_date | datetime | NULL OUTPUT | Returns version date | -## Usage Examples +## Usage -### Basic check on all databases ```sql +-- Basic check on all databases EXEC dbo.sp_PerfCheck; -``` -### Check a specific database only -```sql +-- Check a specific database only EXEC dbo.sp_PerfCheck @database_name = 'YourDatabaseName'; -``` -### Run with debug information -```sql +-- Run with debug information EXEC dbo.sp_PerfCheck @debug = 1; ``` +## Priority System + +All findings are assigned a priority level indicating severity and urgency: + +| Priority | Label | Meaning | +|----------|-------|---------| +| 10 | **Critical** | Server instability — crashes, offline resources, pending configuration changes | +| 20 | **High** | Active performance degradation — severe I/O latency, memory pressure, high deadlock rates | +| 30 | **Medium** | Moderate impact or risky configuration that will likely cause problems | +| 40 | **Low** | Best practice recommendations that improve reliability | +| 50 | **Informational** | Awareness items and non-default settings that may be intentional | + +Results include a `priority_label` column for readability and are sorted by priority (lowest number first). + ## Performance Checks -### Wait Statistics Analysis (6000-series) - -- **High Impact Wait Types** (check_id 6001) - - Analyzes sys.dm_os_wait_stats to identify performance bottlenecks - - Calculates wait time as a percentage of SQL Server uptime - - Categorizes waits by resource type (CPU, I/O, Memory, etc.) - - Identifies abnormal wait patterns indicating specific resource pressure - - Filters out benign or expected wait types - -### Server Configuration (4000-series) - -#### Memory Configuration -- **Lock Pages in Memory Status** (check_id 4105) - - Identifies if SQL Server is using locked pages in memory - - Recommends enabling for production environments with >32GB RAM - -- **Memory Pressure Indicators** (check_id 4001-4002) - - Detects high buffer pool churn, memory grants issues, external memory pressure - - Provides specific thresholds based on server class - -- **Memory Pressure Analysis** (check_id 4101) - - Detects forced memory grants that can impact query performance - - Analyzes memory clerk distribution and pressure points - -- **Security Token Cache Size** (check_id 4104) - - Analyzes TokenAndPermUserStore cache size - - Identifies excessive security token cache which can consume significant memory - - Provides recommendations for high-connection environments - -- **Instant File Initialization** (check_id 4106) - - Verifies if IFI is enabled for data file operations - - Critical for fast file growths and database restores - -- **Min and Max Server Memory** (check_id 1001-1002) - - Check for min server memory too close to max (≥90%) - - Identifies max server memory set too close to physical memory (≥95%) - - Prevents SQL Server from dynamically adjusting memory usage - -- **High Stolen Memory** (check_id 6002) - - Identifies high percentage of memory stolen from buffer pool - - Calculates impact on data caching capability - - Suggests investigating memory usage by CLR, extended stored procedures, or linked servers - -#### CPU Configuration -- **CPU Scheduling Pressure** (check_id 6101-6102) - - High signal waits ratio (>25%) indicating CPU scheduler contention - - Excessive SOS_SCHEDULER_YIELD waits showing CPU pressure - -- **Offline CPU Schedulers** (check_id 4001) - - Detects when CPU schedulers are offline - - Identifies potential affinity mask misconfigurations - - Checks for licensing or VM configuration issues limiting processor availability - -- **MAXDOP Settings** (check_id 1003) - - Identifies default MAXDOP setting (0) on multi-processor systems - - Warns about potential excessive parallelism issues - - Provides recommendations based on logical processor count - -- **Cost Threshold for Parallelism** (check_id 1004) - - Detects low cost threshold settings that may cause excessive parallelism - - Analyzes impact on small query performance - - Recommends appropriate values based on workload characteristics - -#### Server Stability -- **Memory Dumps Analysis** (check_id 4102) - - Detects SQL Server memory dumps indicating stability issues - - Calculates frequency of dumps relative to uptime - - Provides guidance for dump analysis and resolution - -- **Deadlock Detection** (check_id 4103) - - Identifies deadlock frequency and patterns - - Tracks deadlocks per day since server startup - - Indicates potential application concurrency issues - -#### Advanced Configuration Settings -- **Priority Boost** (check_id 1005) - - Detects when priority boost is enabled - - Warns about potential issues with Windows scheduling priorities - - Provides guidance on recommended settings - -- **Lightweight Pooling** (check_id 1006) - - Identifies when lightweight pooling (fiber mode) is enabled - - Warns about potential compatibility issues with OLEDB providers - - Explains performance implications and alternatives - -- **Affinity Mask** (check_id 1008) - - Detects when the affinity mask has been manually configured - - Warns about potential limitations on SQL Server CPU usage - - Provides guidance for CPU binding scenarios - -- **Affinity I/O Mask** (check_id 1009) - - Identifies when the affinity I/O mask has been manually configured - - Warns about binding I/O completion to specific CPUs - - Explains when this specialized configuration might be appropriate - -- **Affinity64 Mask** (check_id 1010) - - Detects when affinity64 mask has been manually configured - - Identifies potential CPU usage limitations on high-CPU systems - - Provides guidance for proper configuration - -- **Affinity64 I/O Mask** (check_id 1011) - - Identifies when affinity64 I/O mask has been manually configured - - Warns about binding I/O completion on high-CPU systems - - Explains performance implications and alternatives - -#### Resource Governor -- **Resource Governor State** (check_id 4107) - - Detects if Resource Governor is enabled - - Provides scripts to examine resource pool and workload group settings - - Analyzes impact on workload performance - -#### Server-level Settings -- **SQL Server Edition and Configuration Options** (server_info) - - Documents product version, edition, and key server properties - - Shows non-default global configuration settings - - Lists globally enabled trace flags - - Identifies offline CPU schedulers or other configuration issues - -#### TempDB Configuration -- **TempDB Files and Settings** (check_id 5000-5003) - - Verifies proper number of TempDB data files based on CPU count - - Checks for equal file sizes and settings - - Identifies suboptimal growth settings - - Analyzes TempDB contention indicators - -- **Potentially Disruptive DBCC Commands** (check_id 5003) - - Detects execution of DBCC FREEPROCCACHE, FREESYSTEMCACHE, DROPCLEANBUFFERS - - Identifies DBCC SHRINKDATABASE and SHRINKFILE operations - - Warns about performance impact on production environments - -### Storage Performance (6000-series) - -- **I/O Stall Statistics** (check_id 6201) - - Tracks read/write stalls per database - - Identifies databases experiencing I/O bottlenecks - - Calculates average stall times for reads and writes - - Differentiates between data and log file performance issues - -- **Storage Performance by File** (check_id 6202-6204) - - Analyzes performance metrics for each database file - - Identifies specific files causing I/O bottlenecks - -- **Auto-growth Events** (server_info) - - Captures slow auto-growth events for data and log files - - Reports frequency and average duration - -### Database Configuration (7000-series) - -- **Basic Database Settings** (check_id 7001-7010) - - Auto-shrink (7001): Performance impact of cyclic shrink/grow operations - - Auto-close (7002): Connection delay impact - - Restricted access mode (7003): Non-multi-user modes - - Statistics settings (7004): Auto create/update statistics disabled - - ANSI settings (7005): Non-standard ANSI settings - - Query Store status (7006): Not enabled - - Recovery time target (7007): Non-default settings - - Transaction durability (7008): Delayed durability modes - - Accelerated Database Recovery (7009): Missing ADR with snapshot isolation - - Ledger feature (7010): Performance overhead of blockchain features - -- **Query Store Health** (check_id 7011-7012) - - State mismatch (7011): Desired state doesn't match actual state - - Suboptimal configuration (7012): Identifies settings that might limit effectiveness - -- **Database Scoped Configurations** (check_id 7020) - - Identifies non-default DSC settings - - Explains the performance impact of each setting - -- **Database File Settings** (check_id 7101-7104) - - Percentage growth for data files (7101): Risk of increasingly larger growth events - - Percentage growth for log files (7102): Higher priority due to zeroing impact - - Non-optimal log growth increments (7103): Missing 64MB setting for instant log initialization - - Extremely large growth increments (7104): Growth settings >10GB that may cause stalls +### Server Configuration + +| Check | Finding | Priority | Description | +|-------|---------|----------|-------------| +| 1000 | Non-Default Configuration | Informational (50) | Reports sp_configure options changed from default | +| 1001 | Min Memory Too Close To Max | Low (40) | Min server memory >= 90% of max | +| 1003 | MAXDOP Not Configured | Low (40) | Default MAXDOP (0) on multi-processor system | +| 1004 | Low Cost Threshold | Low (40) | Cost threshold for parallelism <= 5 | +| 1005 | Priority Boost Enabled | High (20) | Dangerous setting affecting Windows scheduling | +| 1006 | Lightweight Pooling Enabled | Low (40) | Fiber mode rarely beneficial | +| 1007 | Config Pending Reconfigure | Critical (10) | Server not running intended configuration | +| 1008 | Affinity Mask Configured | Informational (50) | Manual CPU binding | +| 1009 | Affinity I/O Mask Configured | Informational (50) | Manual I/O CPU binding | +| 1010 | Affinity64 Mask Configured | Informational (50) | CPU binding for processors 33-64 | +| 1011 | Affinity64 I/O Mask Configured | Informational (50) | I/O binding for processors 33-64 | + +### TempDB Configuration + +| Check | Finding | Priority | Description | +|-------|---------|----------|-------------| +| 2001 | Single TempDB Data File | Medium (30) | Single file causes allocation contention | +| 2002 | Odd Number of TempDB Files | Informational (50) | File count not optimal for CPU count | +| 2003 | More TempDB Files Than CPUs | Informational (50) | More data files than logical processors | +| 2004 | Uneven TempDB File Sizes | Low (40) | Data files vary in size by >10% | +| 2005 | Mixed TempDB Autogrowth | Low (40) | Inconsistent growth settings across files | +| 2006 | Percentage Growth in TempDB | Low (40) | Percentage-based growth in TempDB files | +| 2010 | TempDB Allocation Contention | Medium (30) | Active pagelatch contention detected | + +### Storage Performance + +| Check | Finding | Priority | Description | +|-------|---------|----------|-------------| +| 3001 | Slow Read Latency | High (20) / Medium (30) | >1000 ms = High, >500 ms = Medium per file | +| 3002 | Slow Write Latency | High (20) / Medium (30) | >1000 ms = High, >500 ms = Medium per file | +| 3003 | Multiple Slow Files on Storage Location | High (20) | Systemic storage problem on a drive | + +### Server Health + +| Check | Finding | Priority | Description | +|-------|---------|----------|-------------| +| 4001 | Offline CPU Schedulers | Critical (10) | CPUs offline, reducing processing power | +| 4101 | Memory-Starved Queries (forced) | High (20) | Forced grants causing tempdb spills | +| 4102 | Memory Dumps Detected | Critical (10) | Server crashing in last 90 days | +| 4103 | Memory Grant Timeouts | High (20) | Queries can't get memory | +| 4104 | Large Security Token Cache | High/Medium/Low | >5 GB=20, >2 GB=30, >1 GB=40 | +| 4105 | Lock Pages Not Enabled | Low (40) | Best practice for >=32 GB RAM | +| 4106 | IFI Disabled | Low (40) | Best practice for file operations | +| 4107 | Resource Governor Enabled | Informational (50) | May be intentional | + +### Trace Events + +| Check | Finding | Priority | Description | +|-------|---------|----------|-------------| +| 5001 | Slow Auto-Growth | Medium (30) / Low (40) | Log grows = Medium, data grows = Low | +| 5002 | Auto-Shrink Events | Low (40) | Harmful config executing | +| 5003 | Disruptive DBCC Commands | Medium (30) / Informational (50) | Destructive = Medium, other = Informational | +| 5103 | High Deadlock Rate | High (20) / Medium (30) | >50/day = High, >9/day = Medium | + +### Resource Performance + +| Check | Finding | Priority | Description | +|-------|---------|----------|-------------| +| 6001 | High Impact Wait Types | High (20) / Medium (30) / Low (40) | Top 10 waits by % of uptime | +| 6002 | High Stolen Memory | High (20) / Medium (30) / Low (40) | Buffer pool starvation | +| 6003 | Top Memory Consumers | Informational (50) | Top 5 non-buffer pool memory clerks | +| 6101 | High Signal Wait Ratio | High (20) / Medium (30) / Low (40) | CPU scheduler contention | +| 6102 | High SOS_SCHEDULER_YIELD | High (20) / Medium (30) / Low (40) | CPU pressure from frequent yields | + +### Database Configuration + +| Check | Finding | Priority | Description | +|-------|---------|----------|-------------| +| 7001 | Auto-Shrink Enabled | Medium (30) | Actively harmful config | +| 7002 | Auto-Close Enabled | Low (40) | Causes connection delays | +| 7003 | Restricted Access Mode | High (20) | Apps can't connect | +| 7004 | Auto Stats Disabled | Medium (30) | Causes stale statistics | +| 7005 | ANSI Settings | Informational (50) | Non-standard ANSI settings | +| 7006 | Query Store Not Enabled | Informational (50) | Missed opportunity | +| 7007 | Non-Default Recovery Time | Informational (50) | Awareness | +| 7008 | Delayed Durability | Medium (30) | Data loss risk on crash | +| 7009 | ADR Not Enabled | Low (40) | Recommendation for SI/RCSI databases | +| 7010 | Ledger Feature Enabled | Informational (50) | Awareness of overhead | +| 7011 | Query Store State Mismatch | Medium (30) | QS not working as intended | +| 7012 | Query Store Suboptimal Config | Low (40) | Tuning recommendation | +| 7020 | Non-Default DB Scoped Config | Informational (50) | Awareness | + +### Database File Settings + +| Check | Finding | Priority | Description | +|-------|---------|----------|-------------| +| 7101 | % Growth on Data File | Low (40) | Reports growth % and current file size | +| 7102 | % Growth on Log File | Medium (30) | Reports growth % and current file size | +| 7103 | Non-Optimal Log Growth | Low (40) | Not 64 MB on SQL 2022+/Azure | +| 7104 | Extremely Large Growth | Low (40) | Fixed growth >10 GB | ## Results Organization -Results are organized by check_id ranges to help prioritize focus areas: +Results are organized by check_id ranges: -- **4000-series**: Server configuration issues (memory, CPU, stability) -- **5000-series**: TempDB configuration problems -- **6000-series**: Resource-specific performance issues (waits, I/O, CPU) -- **7000-series**: Database configuration issues +- **1000-series**: Server configuration settings +- **2000-series**: TempDB configuration +- **3000-series**: Storage performance (file-level I/O) +- **4000-series**: Server health (memory, CPU, stability) +- **5000-series**: Trace events (auto-growth, deadlocks, DBCC) +- **6000-series**: Resource performance (waits, I/O, memory) +- **7000-series**: Database configuration -## Results Interpretation - -Results are returned in two sections: +Results are returned in two result sets: 1. **Server Information**: General server metrics and configuration details - - Displays as info_type/value pairs - - Includes version, resource usage, configuration settings - - Shows wait statistics summary and resource utilization - -2. **Performance Check Results**: Specific findings from all checks - - Sorted by priority (lower numbers = higher priority) - - Grouped by category for easier analysis - - Includes details with explanations and recommendations - - Contains URLs to documentation and troubleshooting guidance +2. **Performance Check Results**: Specific findings sorted by priority, with a `priority_label` column for readability + +## Documentation + +Full documentation: [erikdarling.com/sp_perfcheck](https://erikdarling.com/sp_perfcheck/) ## Credits diff --git a/sp_PerfCheck/sp_PerfCheck.sql b/sp_PerfCheck/sp_PerfCheck.sql index 56c7f006..3a06dd47 100644 --- a/sp_PerfCheck/sp_PerfCheck.sql +++ b/sp_PerfCheck/sp_PerfCheck.sql @@ -64,8 +64,8 @@ BEGIN Set version information */ SELECT - @version = N'2.2.5', - @version_date = N'20260206'; + @version = N'2.3', + @version_date = N'20260217'; /* Help section, for help. @@ -279,8 +279,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @has_percent_growth bit, @has_fixed_growth bit, /* Storage performance variables */ - @slow_read_ms decimal(10, 2) = 100.0, /* Threshold for slow reads (ms) */ - @slow_write_ms decimal(10, 2) = 100.0, /* Threshold for slow writes (ms) */ + @slow_read_ms decimal(10, 2) = 500.0, /* Threshold for slow reads (ms) */ + @slow_write_ms decimal(10, 2) = 500.0, /* Threshold for slow writes (ms) */ /* Set threshold for "slow" autogrowth (in ms) */ @slow_autogrow_ms integer = 1000, /* 1 second */ @trace_path nvarchar(260), @@ -877,7 +877,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ) SELECT check_id = 4001, - priority = 20, /* Very high priority */ + priority = 10, /* Critical: missing CPU resources */ category = N'CPU Configuration', finding = N'Offline CPU Schedulers', details = @@ -886,7 +886,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. CONVERT(nvarchar(10), (SELECT cpu_count FROM sys.dm_os_sys_info)) + N' logical processors. This reduces available processing power. ' + N'Check affinity mask configuration, licensing, or VM CPU cores/sockets', - url = N'https://erikdarling.com/sp_PerfCheck/#OfflineCPU' + url = N'https://erikdarling.com/sp_perfcheck/#OfflineCPU' FROM sys.dm_os_schedulers AS dos WHERE dos.is_online = 0 HAVING @@ -909,7 +909,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ) SELECT check_id = 4101, - priority = 30, /* High priority */ + priority = 20, /* High: active memory spills */ category = N'Memory Pressure', finding = N'Memory-Starved Queries Detected', details = @@ -917,7 +917,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. CONVERT(nvarchar(10), MAX(ders.forced_grant_count)) + N' forced memory grants. ' + N'Queries are being forced to run with less memory than requested, which can cause spills to tempdb and poor performance.', - url = N'https://erikdarling.com/sp_PerfCheck#MemoryStarved' + url = N'https://erikdarling.com/sp_perfcheck/#MemoryStarved' FROM sys.dm_exec_query_resource_semaphores AS ders WHERE ders.forced_grant_count > 0 HAVING @@ -936,7 +936,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ) SELECT check_id = 4103, - priority = 30, /* High priority */ + priority = 20, /* High: queries can't get memory */ category = N'Memory Pressure', finding = N'Memory-Starved Queries Detected', details = @@ -944,7 +944,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. CONVERT(nvarchar(10), MAX(ders.timeout_error_count)) + N' memory grant timeouts. ' + N'Queries are waiting for memory for a long time and giving up.', - url = N'https://erikdarling.com/sp_PerfCheck#MemoryStarved' + url = N'https://erikdarling.com/sp_perfcheck/#MemoryStarved' FROM sys.dm_exec_query_resource_semaphores AS ders WHERE ders.timeout_error_count > 0 HAVING @@ -971,7 +971,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ) SELECT check_id = 4102, - priority = 20, /* Very high priority */ + priority = 10, /* Critical: server crashing */ category = N'Server Stability', finding = N'Memory Dumps Detected In Last 90 Days', details = @@ -982,7 +982,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. N' at ' + MAX(dsmd.filename) + N'. Check the SQL Server error log and Windows event logs.', - url = N'https://erikdarling.com/sp_PerfCheck#MemoryDumps' + url = N'https://erikdarling.com/sp_perfcheck/#MemoryDumps' FROM sys.dm_server_memory_dumps AS dsmd WHERE dsmd.creation_time >= DATEADD(DAY, -90, SYSDATETIME()) HAVING @@ -1022,7 +1022,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 0 ) ) > 100 - THEN 20 /* Very high priority */ + THEN 20 /* High: >100 deadlocks/day */ WHEN ( 1.0 * @@ -1038,8 +1038,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 0 ) ) > 50 - THEN 30 /* High priority */ - ELSE 40 /* Medium-high priority */ + THEN 20 /* High: >50 deadlocks/day is still severe */ + ELSE 30 /* Medium: >9 deadlocks/day */ END, category = N'Concurrency', finding = N'High Number of Deadlocks', @@ -1064,7 +1064,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. N' hours since startup. ' + N'High deadlock rates indicate concurrency issues that should be investigated.' END, - url = N'https://erikdarling.com/sp_PerfCheck#Deadlocks' + url = N'https://erikdarling.com/sp_perfcheck/#Deadlocks' FROM sys.dm_os_performance_counters AS p CROSS JOIN sys.dm_os_sys_info AS osi WHERE RTRIM(p.counter_name) = N'Number of Deadlocks/sec' @@ -1102,12 +1102,10 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. priority = CASE WHEN CONVERT(decimal(10, 2), (domc.pages_kb / 1024.0 / 1024.0)) > 5 - THEN 20 /* Very high priority >5GB */ - WHEN CONVERT(decimal(10, 2), (domc.pages_kb / 1024.0 / 1024.0)) BETWEEN 2 AND 5 - THEN 30 /* High priority >2GB */ - WHEN CONVERT(decimal(10, 2), (domc.pages_kb / 1024.0 / 1024.0)) BETWEEN 1 AND 2 - THEN 40 /* Medium-high priority >1GB */ - ELSE 50 /* Medium priority */ + THEN 20 /* High: >5GB security cache is severe */ + WHEN CONVERT(decimal(10, 2), (domc.pages_kb / 1024.0 / 1024.0)) > 2 + THEN 30 /* Medium: 2-5GB security cache */ + ELSE 40 /* Low: 1-2GB security cache */ END, category = N'Memory Usage', finding = N'Large Security Token Cache', @@ -1117,11 +1115,11 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. N' GB. Large security caches can consume significant memory and may indicate security-related issues ' + N'such as excessive application role usage or frequent permission changes. ' + N'Consider using dbo.ClearTokenPerm stored procedure to manage this issue.', - url = N'https://erikdarling.com/sp_PerfCheck#SecurityToken' + url = N'https://erikdarling.com/sp_perfcheck/#SecurityToken' FROM sys.dm_os_memory_clerks AS domc WHERE domc.type = N'USERSTORE_TOKENPERM' AND domc.name = N'TokenAndPermUserStore' - AND domc.pages_kb >= 500000; /* Only if bigger than 500MB */ + AND domc.pages_kb >= 1048576; /* Only if bigger than 1 GB */ /* Get physical memory for LPIM check */ SELECT @@ -1150,14 +1148,14 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ) SELECT check_id = 4105, - priority = 50, /* Medium priority */ + priority = 40, /* Low: best practice */ category = N'Memory Configuration', finding = N'Lock Pages in Memory Not Enabled', details = N'SQL Server is not using locked pages in memory (LPIM). This can lead to Windows ' + N'taking memory away from SQL Server under memory pressure, causing performance issues. ' + N'For production SQL Servers with more than 64GB of memory, LPIM should be enabled.', - url = N'https://erikdarling.com/sp_PerfCheck#LPIM' + url = N'https://erikdarling.com/sp_perfcheck/#LPIM' FROM sys.dm_os_sys_info AS osi WHERE osi.sql_memory_model_desc = N'CONVENTIONAL' /* Conventional means not using LPIM */ AND @physical_memory_gb >= 32 /* Only recommend for servers with >=32GB RAM */; @@ -1209,14 +1207,14 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ) SELECT TOP (1) check_id = 4106, - priority = 50, /* Medium priority */ + priority = 40, /* Low: best practice */ category = N'Storage Configuration', finding = N'Instant File Initialization Disabled', details = N'Instant File Initialization is not enabled. This can significantly slow down database file ' + N'creation and growth operations, as SQL Server must zero out data files before using them. ' + N'Enable this feature by granting the "Perform Volume Maintenance Tasks" permission to the SQL Server service account.', - url = N'https://erikdarling.com/sp_PerfCheck#IFI' + url = N'https://erikdarling.com/sp_perfcheck/#IFI' FROM sys.dm_server_services AS dss WHERE dss.filename LIKE N'%sqlservr.exe%' AND dss.servicename LIKE N'SQL Server%' @@ -1252,7 +1250,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ) SELECT check_id = 4107, - priority = 50, /* Medium priority */ + priority = 50, /* Informational: may be intentional */ category = N'Resource Governor', finding = N'Resource Governor Enabled', details = @@ -1268,7 +1266,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. N'/* Classifier function (if configured) */' + NCHAR(13) + NCHAR(10) + N'SELECT cf.* FROM sys.resource_governor_configuration AS gc' + NCHAR(13) + NCHAR(10) + N'CROSS APPLY (SELECT OBJECT_NAME(gc.classifier_function_id) AS classifier_function_name) AS cf;', - url = N'https://erikdarling.com/sp_PerfCheck#ResourceGovernor' + url = N'https://erikdarling.com/sp_perfcheck/#ResourceGovernor' FROM sys.resource_governor_configuration AS rgc WHERE rgc.is_enabled = 1; END @@ -1559,8 +1557,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. priority = CASE WHEN te.event_class = 93 - THEN 40 /* Log file autogrow (higher priority) */ - ELSE 50 /* Data file autogrow */ + THEN 30 /* Medium: log file autogrow (zeroing required) */ + ELSE 40 /* Low: data file autogrow */ END, category = N'Database File Configuration', finding = @@ -1584,7 +1582,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. N'Growth amount: ' + CONVERT(nvarchar(20), CONVERT(decimal(18,2), te.file_growth * 8.0 / 1024.0)) + N' MB. ', - url = N'https://erikdarling.com/sp_PerfCheck#AutoGrowth' + url = N'https://erikdarling.com/sp_perfcheck/#AutoGrowth' FROM #trace_events AS te WHERE te.event_class IN (92, 93) /* Auto-grow events */ AND te.duration_ms > @slow_autogrow_ms @@ -1606,7 +1604,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ) SELECT TOP (10) check_id = 5002, - priority = 60, /* Medium priority */ + priority = 40, /* Low: harmful config executing */ category = N'Database File Configuration', finding = CASE @@ -1624,7 +1622,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. N'. ' + N'Auto-shrink is generally not recommended as it can lead to file fragmentation and ' + N'repeated grow/shrink cycles. Consider disabling auto-shrink on this database.', - url = N'https://erikdarling.com/sp_PerfCheck#AutoShrink' + url = N'https://erikdarling.com/sp_perfcheck/#AutoShrink' FROM #trace_events AS te WHERE te.event_class IN (94, 95) /* Auto-shrink events */ ORDER BY @@ -1650,8 +1648,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. OR dbcc_cmd.dbcc_pattern LIKE N'%FREESYSTEMCACHE%' OR dbcc_cmd.dbcc_pattern LIKE N'%DROPCLEANBUFFERS%' OR dbcc_cmd.dbcc_pattern LIKE N'%WRITEPAGE%' - THEN 40 /* Higher priority */ - ELSE 60 /* Medium priority */ + THEN 30 /* Medium: destructive DBCC commands */ + ELSE 50 /* Informational: other DBCC commands */ END, N'System Management', N'Potentially Disruptive DBCC Commands', @@ -1674,7 +1672,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. CONVERT(nvarchar(30), MAX(te.event_time), 120) + N'. These commands can impact server performance or database integrity. ' + N'Review why these commands are being executed, especially if on a production system.', - N'https://erikdarling.com/sp_PerfCheck/#DisruptiveDBCC' + N'https://erikdarling.com/sp_perfcheck/#DisruptiveDBCC' FROM #trace_events AS te CROSS APPLY ( @@ -2006,12 +2004,12 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. priority = CASE WHEN ws.wait_time_percent_of_uptime > 100 - THEN 20 /* Very high priority if >100% of uptime */ + THEN 20 /* High: >100% of uptime */ WHEN ws.wait_time_percent_of_uptime > 75 - THEN 30 /* High priority if >75% of uptime */ + THEN 20 /* High: >75% of uptime */ WHEN ws.wait_time_percent_of_uptime >= 50 - THEN 40 /* Medium-high priority if >=50% of uptime */ - ELSE 50 /* Medium priority otherwise */ + THEN 30 /* Medium: >=50% of uptime */ + ELSE 40 /* Low: >=10% of uptime */ END, category = N'Wait Statistics', finding = @@ -2033,7 +2031,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. N' ms per wait. ' + N'Description: ' + ws.description, - url = N'https://erikdarling.com/sp_PerfCheck#WaitStats' + url = N'https://erikdarling.com/sp_perfcheck/#WaitStats' FROM #wait_stats AS ws WHERE ws.wait_time_percent_of_uptime >= 10.0 /* Only include waits that are at least 10% of uptime */ AND ws.wait_type <> N'SLEEP_TASK' @@ -2169,10 +2167,10 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 6101, CASE WHEN @signal_wait_ratio >= 50.0 - THEN 20 /* Very high priority if >=50% signal waits */ + THEN 20 /* High: >=50% signal waits */ WHEN @signal_wait_ratio >= 30.0 - THEN 30 /* High priority if >=30% signal waits */ - ELSE 40 /* Medium-high priority */ + THEN 30 /* Medium: >=30% signal waits */ + ELSE 40 /* Low: notable signal waits */ END, N'CPU Scheduling', N'High Signal Wait Ratio', @@ -2181,7 +2179,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. N'%. This indicates significant CPU scheduling pressure. ' + N'Processes are waiting to get scheduled on the CPU, which can impact query performance. ' + N'Consider investigating high-CPU queries, reducing server load, or adding CPU resources.', - N'https://erikdarling.com/sp_PerfCheck#CPUPressure' + N'https://erikdarling.com/sp_perfcheck/#CPUPressure' ); END; @@ -2203,10 +2201,10 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 6102, CASE WHEN @sos_scheduler_yield_pct_of_uptime >= 50.0 - THEN 30 /* High priority if >=50% of uptime */ + THEN 20 /* High: >=50% of uptime in scheduler yields */ WHEN @sos_scheduler_yield_pct_of_uptime >= 30.0 - THEN 40 /* Medium-high priority if >=30% of uptime */ - ELSE 50 /* Medium priority */ + THEN 30 /* Medium: >=30% of uptime */ + ELSE 40 /* Low: >=25% of uptime */ END, N'CPU Scheduling', N'High SOS_SCHEDULER_YIELD Waits', @@ -2215,7 +2213,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. N'% of server uptime. This indicates tasks frequently giving up their quantum of CPU time. ' + N'This can be caused by CPU-intensive queries, causing threads to context switch frequently. ' + N'Consider tuning queries with high CPU usage or adding CPU resources.', - N'https://erikdarling.com/sp_PerfCheck#CPUPressure' + N'https://erikdarling.com/sp_perfcheck/#CPUPressure' ); END; END; @@ -2347,10 +2345,10 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 6002, CASE WHEN @stolen_memory_pct > 30 - THEN 30 /* High priority if >30% stolen */ + THEN 20 /* High: actively starving buffer pool */ WHEN @stolen_memory_pct > 15 - THEN 40 /* Medium-high priority if >15% stolen */ - ELSE 50 /* Medium priority */ + THEN 30 /* Medium: significant stolen memory */ + ELSE 40 /* Low: moderate stolen memory */ END, N'Memory Usage', N'High Stolen Memory Percentage', @@ -2360,7 +2358,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. CONVERT(nvarchar(10), CONVERT(decimal(10, 1), ISNULL(@stolen_memory_pct, 0))) + N'% of total memory). This reduces memory available for data caching and can impact performance. ' + N'Consider investigating memory usage by CLR, extended stored procedures, linked servers, or other memory clerks.', - N'https://erikdarling.com/sp_PerfCheck#MemoryStarved' + N'https://erikdarling.com/sp_perfcheck/#StolenMemory' ); /* Also add the top 5 non-buffer pool memory consumers for visibility */ @@ -2376,7 +2374,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ) SELECT TOP (5) check_id = 6003, - priority = 60, /* Informational priority */ + priority = 50, /* Informational: memory context */ category = N'Memory Usage', finding = N'Top Memory Consumer: ' + @@ -2395,7 +2393,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ) ) + N' GB of memory. This is one of the top consumers of memory outside the buffer pool.', - url = N'https://erikdarling.com/sp_PerfCheck#MemoryStarved' + url = N'https://erikdarling.com/sp_perfcheck/#StolenMemory' FROM sys.dm_os_memory_clerks AS domc WHERE domc.type <> N'MEMORYCLERK_SQLBUFFERPOOL' GROUP BY @@ -2529,7 +2527,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. VALUES ( 9997, - 70, /* Medium priority */ + 50, /* Informational: collection error */ N'Errors', N'Error Collecting IO Statistics', N'Unable to collect IO stall statistics: ' + ERROR_MESSAGE() @@ -2588,60 +2586,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ORDER BY i.avg_io_stall_ms DESC; - /* Add findings for significant I/O stalls */ - INSERT INTO - #results - ( - check_id, - priority, - category, - finding, - database_name, - details, - url - ) - SELECT TOP (10) - check_id = 6201, - priority = - CASE - WHEN io.avg_io_stall_ms >= 100.0 - THEN 30 /* High priority if >100ms */ - WHEN io.avg_io_stall_ms >= 50.0 - THEN 40 /* Medium-high priority if >50ms */ - ELSE 50 /* Medium priority */ - END, - category = N'Storage Performance', - finding = N'High Database I/O Stalls', - database_name = io.database_name, - details = - N'Database ' + - io.database_name + - N' has average I/O stall of ' + - CONVERT(nvarchar(10), CONVERT(decimal(10, 2), io.avg_io_stall_ms)) + - N' ms. ' + - N'Read latency: ' + - CONVERT(nvarchar(10), CONVERT(decimal(10, 2), io.avg_read_stall_ms)) + - N' ms, Write latency: ' + - CONVERT(nvarchar(10), CONVERT(decimal(10, 2), io.avg_write_stall_ms)) + - N' ms. ' + - N'Total read: ' + - CONVERT(nvarchar(20), CONVERT(decimal(18, 2), io.read_io_mb)) + - N' MB, Total write: ' + - CONVERT(nvarchar(20), CONVERT(decimal(18, 2), io.write_io_mb)) + - N' MB. ' + - N'This indicates slow I/O subsystem performance for this database.', - url = N'https://erikdarling.com/sp_PerfCheck#IOStalls' - FROM #io_stalls_by_db AS io - WHERE - /* Only include databases with significant I/O and significant stalls */ - io.total_io_mb > 1024.0 /* Only databases with at least 1024MB total I/O */ - AND - ( - io.avg_read_stall_ms >= @slow_read_ms - OR io.avg_write_stall_ms >= @slow_write_ms - ) - ORDER BY - io.avg_io_stall_ms DESC; + /* Check 6201 (High Database I/O Stalls) removed — duplicated by file-level checks 3001/3002/3003 */ END; /* @@ -2769,7 +2714,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. VALUES ( 9996, - 70, /* Medium priority */ + 50, /* Informational: collection error */ N'Errors', N'Error Collecting File IO Statistics', N'Unable to collect file IO statistics: ' + ERROR_MESSAGE() @@ -2794,8 +2739,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. priority = CASE WHEN i.avg_read_latency_ms > @slow_read_ms * 2 - THEN 40 /* Very slow */ - ELSE 50 /* Moderately slow */ + THEN 20 /* High: >1000ms is severe */ + ELSE 30 /* Medium: >500ms is significant */ END, category = N'Storage Performance', finding = N'Slow Read Latency', @@ -2814,7 +2759,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. N'This is above the ' + CONVERT(nvarchar(10), CONVERT(integer, @slow_read_ms)) + N' ms threshold and may indicate storage performance issues.', - url = N'https://erikdarling.com/sp_PerfCheck#StoragePerformance' + url = N'https://erikdarling.com/sp_perfcheck/#StoragePerformance' FROM #io_stats AS i WHERE i.avg_read_latency_ms > @slow_read_ms AND i.num_of_reads > 1000; /* Only alert if there's been a significant number of reads */ @@ -2837,8 +2782,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. priority = CASE WHEN i.avg_write_latency_ms > @slow_write_ms * 2 - THEN 40 /* Very slow */ - ELSE 50 /* Moderately slow */ + THEN 20 /* High: >1000ms is severe */ + ELSE 30 /* Medium: >500ms is significant */ END, category = N'Storage Performance', finding = N'Slow Write Latency', @@ -2857,7 +2802,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. N'This is above the ' + CONVERT(nvarchar(10), CONVERT(integer, @slow_write_ms)) + N' ms threshold and may indicate storage performance issues.', - url = N'https://erikdarling.com/sp_PerfCheck#StoragePerformance' + url = N'https://erikdarling.com/sp_perfcheck/#StoragePerformance' FROM #io_stats AS i WHERE i.avg_write_latency_ms > @slow_write_ms AND i.num_of_writes > 1000; /* Only alert if there's been a significant number of writes */ @@ -2875,7 +2820,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ) SELECT check_id = 3003, - priority = 40, /* High priority */ + priority = 20, /* High: systemic storage problem */ category = N'Storage Performance', finding = N'Multiple Slow Files on Storage Location ' + @@ -2890,7 +2835,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. CONVERT(nvarchar(10), CONVERT(decimal(10, 2), AVG(i.avg_io_latency_ms))) + N' ms. ' + N'This may indicate an overloaded drive or underlying storage issue.', - url = N'https://erikdarling.com/sp_PerfCheck#StoragePerformance' + url = N'https://erikdarling.com/sp_perfcheck/#StoragePerformance' FROM #io_stats AS i WHERE ( @@ -3086,7 +3031,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ) SELECT check_id = 1000, - priority = 70, /* Informational priority */ + priority = 50, /* Informational: non-default config */ category = N'Server Configuration', finding = N'Non-Default Configuration: ' + c.name, details = @@ -3119,7 +3064,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. WHEN c.name = N'lightweight pooling' THEN N', Default: 0' ELSE N', Default: Unknown' END, - url = N'https://erikdarling.com/sp_PerfCheck#ServerSettings' + url = N'https://erikdarling.com/sp_perfcheck/#ServerSettings' FROM sys.configurations AS c WHERE /* Access check cache settings */ @@ -3225,7 +3170,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. VALUES ( 9995, - 70, /* Medium priority */ + 50, /* Informational: collection error */ N'Errors', N'Error Collecting TempDB File Information', N'Unable to collect TempDB file information: ' + @@ -3325,7 +3270,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. VALUES ( 2001, - 50, /* High priority */ + 30, /* Medium: single file causes contention */ N'TempDB Configuration', N'Single TempDB Data File', N'TempDB has only one data file on a ' + CONVERT(nvarchar(10), @processors) + @@ -3334,7 +3279,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. WHEN @processors > 8 THEN N'8' ELSE CONVERT(nvarchar(10), @processors) END + N' data files total.', - N'https://erikdarling.com/sp_PerfCheck#tempdb' + N'https://erikdarling.com/sp_perfcheck/#TempDB' ); END; @@ -3355,14 +3300,14 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. VALUES ( 2002, - 65, /* Medium priority */ + 50, /* Informational */ N'TempDB Configuration', N'Odd Number of TempDB Files', N'TempDB has ' + CONVERT(nvarchar(10), @tempdb_data_file_count) + N' data files. This is an odd number and not equal to the ' + CONVERT(nvarchar(10), @processors) + ' logical processors. ' + N'Consider using an even number of files for better performance.', - N'https://erikdarling.com/sp_PerfCheck#tempdb' + N'https://erikdarling.com/sp_perfcheck/#TempDB' ); END; @@ -3382,7 +3327,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. VALUES ( 2003, - 70, /* Informational */ + 50, /* Informational */ N'TempDB Configuration', N'More TempDB Files Than CPUs', N'TempDB has ' + @@ -3390,7 +3335,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. N' data files, which is more than the ' + CONVERT(nvarchar(10), @processors) + N' logical processors. ', - N'https://erikdarling.com/sp_PerfCheck#tempdb' + N'https://erikdarling.com/sp_perfcheck/#TempDB' ); END; @@ -3410,7 +3355,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. VALUES ( 2004, - 55, /* High-medium priority */ + 40, /* Low: uneven file sizes */ N'TempDB Configuration', N'Uneven TempDB Data File Sizes', N'TempDB data files vary in size by ' + @@ -3420,7 +3365,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. N' GB, Largest: ' + CONVERT(nvarchar(10), CONVERT(integer, @max_data_file_size)) + N' GB. For best performance, TempDB data files should be the same size.', - N'https://erikdarling.com/sp_PerfCheck#tempdb' + N'https://erikdarling.com/sp_perfcheck/#TempDB' ); END; @@ -3441,12 +3386,12 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. VALUES ( 2005, - 55, /* High-medium priority */ + 40, /* Low: inconsistent growth settings */ N'TempDB Configuration', N'Mixed TempDB Autogrowth Settings', N'TempDB data files have inconsistent autogrowth settings - some use percentage growth and others use fixed size growth. ' + N'This can lead to uneven file sizes over time. Use consistent settings for all files.', - N'https://erikdarling.com/sp_PerfCheck#tempdb' + N'https://erikdarling.com/sp_perfcheck/#TempDB' ); END; @@ -3466,12 +3411,12 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. VALUES ( 2006, - 50, /* High-medium priority */ + 40, /* Low: percent growth in tempdb */ N'TempDB Configuration', N'Percentage Auto-Growth Setting in TempDB', N'TempDB data files are using percentage growth settings. This can lead to increasingly larger growth events as files grow. ' + N'TempDB is recreated on server restart, so using predictable fixed-size growth is recommended for better performance.', - N'https://erikdarling.com/sp_PerfCheck#tempdb' + N'https://erikdarling.com/sp_perfcheck/#TempDB' ); END; @@ -3494,7 +3439,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. VALUES ( 2010, - 40, /* High priority */ + 30, /* Medium: active contention */ N'TempDB Performance', N'TempDB Allocation Contention Detected', N'Server has spent ' + @@ -3508,7 +3453,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. WHEN @processors > 8 THEN N'8' ELSE CONVERT(nvarchar(10), @processors) END + N' total to reduce allocation contention.', - N'https://erikdarling.com/sp_PerfCheck#tempdb-contention' + N'https://erikdarling.com/sp_perfcheck/#TempDB' ); END; @@ -3528,7 +3473,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. VALUES ( 1001, - 50, /* High priority */ + 40, /* Low: config recommendation */ N'Server Configuration', N'Min Server Memory Too Close To Max', N'Min server memory (' + @@ -3536,7 +3481,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. N' MB) is >= 90% of max server memory (' + CONVERT(nvarchar(20), @max_server_memory) + N' MB). This prevents SQL Server from dynamically adjusting memory.', - N'https://erikdarling.com/sp_PerfCheck/#MinMaxMemory' + N'https://erikdarling.com/sp_perfcheck/#MinMaxMemory' ); END; @@ -3564,7 +3509,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. N' MB) is >= 95% of physical memory (' + CONVERT(nvarchar(20), CONVERT(bigint, @physical_memory_gb * 1024)) + N' MB). This may not leave enough memory for the OS and other processes.', - N'https://erikdarling.com/sp_PerfCheck/#MinMaxMemory' + N'https://erikdarling.com/sp_perfcheck/#MinMaxMemory' ); END; @@ -3585,13 +3530,13 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. VALUES ( 1003, - 60, /* Medium priority */ + 40, /* Low: config recommendation */ N'Server Configuration', N'MAXDOP Not Configured', N'Max degree of parallelism is set to 0 (default) on a server with ' + CONVERT(nvarchar(10), @processors) + N' logical processors. This can lead to excessive parallelism.', - N'https://erikdarling.com/sp_PerfCheck/#MAXDOP' + N'https://erikdarling.com/sp_perfcheck/#MAXDOP' ); END; @@ -3611,13 +3556,13 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. VALUES ( 1004, - 60, /* Medium priority */ + 40, /* Low: config recommendation */ N'Server Configuration', N'Low Cost Threshold for Parallelism', N'Cost threshold for parallelism is set to ' + CONVERT(nvarchar(10), @cost_threshold) + N'. Low values can cause excessive parallelism for small queries.', - N'https://erikdarling.com/sp_PerfCheck/#CostThreshold' + N'https://erikdarling.com/sp_perfcheck/#CostThreshold' ); END; @@ -3637,12 +3582,12 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. VALUES ( 1005, - 30, /* High priority */ + 20, /* High: priority boost is dangerous */ N'Server Configuration', N'Priority Boost Enabled', N'Priority boost is enabled. This can cause issues with Windows scheduling priorities and is not recommended.', - N'https://erikdarling.com/sp_PerfCheck/#PriorityBoost' + N'https://erikdarling.com/sp_perfcheck/#PriorityBoost' ); END; @@ -3662,12 +3607,12 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. VALUES ( 1006, - 50, /* Medium priority */ + 40, /* Low: rarely beneficial */ N'Server Configuration', N'Lightweight Pooling Enabled', N'Lightweight pooling (fiber mode) is enabled. This is rarely beneficial and can cause issues with OLEDB providers and other components.', - N'https://erikdarling.com/sp_PerfCheck/#LightweightPooling' + N'https://erikdarling.com/sp_perfcheck/#LightweightPooling' ); END; @@ -3687,13 +3632,13 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. VALUES ( 1008, - 50, /* Medium priority */ + 50, /* Informational: may be intentional */ N'Server Configuration', N'Affinity Mask Configured', N'Affinity mask has been manually configured to ' + CONVERT(nvarchar(20), @affinity_mask) + N'. This can limit SQL Server CPU usage and should only be used when necessary for specific CPU binding scenarios.', - N'https://erikdarling.com/sp_PerfCheck/#AffinityMask' + N'https://erikdarling.com/sp_perfcheck/#AffinityMask' ); END; @@ -3713,13 +3658,13 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. VALUES ( 1009, - 50, /* Medium priority */ + 50, /* Informational: may be intentional */ N'Server Configuration', N'Affinity I/O Mask Configured', N'Affinity I/O mask has been manually configured to ' + CONVERT(nvarchar(20), @affinity_io_mask) + N'. This binds I/O completion to specific CPUs and should only be used for specialized workloads.', - N'https://erikdarling.com/sp_PerfCheck/#AffinityIOMask' + N'https://erikdarling.com/sp_perfcheck/#AffinityIOMask' ); END; @@ -3739,13 +3684,13 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. VALUES ( 1010, - 50, /* Medium priority */ + 50, /* Informational: may be intentional */ N'Server Configuration', N'Affinity64 Mask Configured', N'Affinity64 mask has been manually configured to ' + CONVERT(nvarchar(20), @affinity64_mask) + N'. This can limit SQL Server CPU usage on high-CPU systems and should be carefully evaluated.', - N'https://erikdarling.com/sp_PerfCheck/#Affinity64Mask' + N'https://erikdarling.com/sp_perfcheck/#Affinity64Mask' ); END; @@ -3765,13 +3710,13 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. VALUES ( 1011, - 50, /* Medium priority */ + 50, /* Informational: may be intentional */ N'Server Configuration', N'Affinity64 I/O Mask Configured', N'Affinity64 I/O mask has been manually configured to ' + CONVERT(nvarchar(20), @affinity64_io_mask) + N'. This binds I/O completion on high-CPU systems and should be carefully evaluated.', - N'https://erikdarling.com/sp_PerfCheck/#Affinity64Mask' + N'https://erikdarling.com/sp_perfcheck/#Affinity64IOMask' ); END; @@ -3788,7 +3733,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ) SELECT check_id = 1007, - priority = 20, /* Very high priority */ + priority = 10, /* Critical: server not running intended config */ category = N'Server Configuration', finding = N'Configuration Pending Reconfigure', details = @@ -3800,7 +3745,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. N', ' + N'Pending value: ' + CONVERT(nvarchar(50), c.value), - url = N'https://erikdarling.com/sp_PerfCheck#ServerSettings' + url = N'https://erikdarling.com/sp_perfcheck/#ServerSettings' FROM sys.configurations AS c WHERE c.value <> c.value_in_use AND NOT @@ -4208,13 +4153,13 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ) SELECT check_id = 7001, - priority = 50, + priority = 30, /* Medium: actively harmful config */ category = N'Database Configuration', finding = N'Auto-Shrink Enabled', database_name = d.name, details = N'Database has auto-shrink enabled, which can cause significant performance problems.', - url = N'https://erikdarling.com/sp_PerfCheck#AutoShrink' + url = N'https://erikdarling.com/sp_perfcheck/#AutoShrink' FROM #databases AS d WHERE d.database_id = @current_database_id AND d.is_auto_shrink_on = 1; @@ -4233,14 +4178,14 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ) SELECT check_id = 7002, - priority = 50, + priority = 40, /* Low: bad practice but less destructive */ category = N'Database Configuration', finding = N'Auto-Close Enabled', database_name = d.name, details = N'Database has auto-close enabled, which can cause connection delays while the database is reopened. This setting can impact performance for applications that frequently connect to and disconnect from the database.', - url = N'https://erikdarling.com/sp_PerfCheck#AutoClose' + url = N'https://erikdarling.com/sp_perfcheck/#AutoClose' FROM #databases AS d WHERE d.database_id = @current_database_id AND d.is_auto_close_on = 1; @@ -4259,7 +4204,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ) SELECT check_id = 7003, - priority = 30, /* High priority */ + priority = 20, /* High: apps can't connect */ category = N'Database Configuration', finding = N'Restricted Access Mode: ' + @@ -4269,7 +4214,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. N'Database is not in MULTI_USER mode. Current mode: ' + d.user_access_desc + N'. This restricts normal database access and may prevent applications from connecting.', - url = N'https://erikdarling.com/sp_PerfCheck#RestrictedAccess' + url = N'https://erikdarling.com/sp_perfcheck/#RestrictedAccess' FROM #databases AS d WHERE d.database_id = @current_database_id AND d.user_access_desc <> N'MULTI_USER'; @@ -4288,7 +4233,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ) SELECT check_id = 7004, - priority = 40, /* Medium-high priority */ + priority = 30, /* Medium: causes stale stats */ category = N'Database Configuration', finding = CASE @@ -4313,7 +4258,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. AND d.is_auto_create_stats_on = 1 THEN N'Auto update statistics is disabled. This can lead to poor query performance due to outdated statistics.' END, - url = N'https://erikdarling.com/sp_PerfCheck#Statistics' + url = N'https://erikdarling.com/sp_perfcheck/#Statistics' FROM #databases AS d WHERE d.database_id = @current_database_id AND @@ -4336,7 +4281,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ) SELECT check_id = 7005, - priority = 50, /* Medium priority */ + priority = 50, /* Informational */ category = N'Database Configuration', finding = N'ANSI Settings Require Review', database_name = d.name, @@ -4352,7 +4297,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. CASE WHEN d.is_quoted_identifier_on = 0 THEN N'QUOTED_IDENTIFIER OFF (recommended ON), ' ELSE N'' END + N'These settings may lead to inconsistent behavior, reduced feature compatibility, or unexpected query results ' + N'if they do not align with recommended best practices.', - url = N'https://erikdarling.com/sp_PerfCheck#ANSISettings' + url = N'https://erikdarling.com/sp_perfcheck/#ANSISettings' FROM #databases AS d WHERE d.database_id = @current_database_id AND @@ -4381,14 +4326,14 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ) SELECT check_id = 7006, - priority = 60, /* Informational priority */ + priority = 50, /* Informational: missed opportunity */ category = N'Database Configuration', finding = N'Query Store Not Enabled', database_name = d.name, details = N'Query Store is not enabled.' + N' Consider enabling Query Store to track query performance' + N' over time and identify regression issues.', - url = N'https://erikdarling.com/sp_PerfCheck#QueryStore' + url = N'https://erikdarling.com/sp_perfcheck/#QueryStore' FROM #databases AS d WHERE d.database_id = @current_database_id AND d.is_query_store_on = 0 @@ -4404,14 +4349,14 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SELECT check_id = 7006, - priority = 60, /* Informational priority */ + priority = 50, /* Informational: missed opportunity */ category = N''Database Configuration'', finding = N''Query Store Not Enabled'', database_name = @current_database_name, details = N''Query Store is not enabled. Consider enabling Query Store to track query performance over time and identify regression issues.'', - url = N''https://erikdarling.com/sp_PerfCheck#QueryStore'' + url = N''https://erikdarling.com/sp_perfcheck/#QueryStore'' FROM ' + QUOTENAME(@current_database_name) + N'.sys.database_query_store_options AS qso WHERE qso.actual_state = 0 /* OFF */;'; @@ -4444,7 +4389,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SELECT check_id = 7011, - priority = 40, /* Medium-high priority */ + priority = 30, /* Medium: QS not working as intended */ category = N''Database Configuration'', finding = N''Query Store State Mismatch'', database_name = @current_database_name, @@ -4468,7 +4413,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. WHEN 524288 THEN N''Database has reached disk size limit.'' ELSE N''Unknown reason code: '' + CONVERT(nvarchar(20), qso.readonly_reason) END, - url = N''https://erikdarling.com/sp_PerfCheck#QueryStoreHealth'' + url = N''https://erikdarling.com/sp_perfcheck/#QueryStoreHealth'' FROM ' + QUOTENAME(@current_database_name) + N'.sys.database_query_store_options AS qso WHERE qso.desired_state <> 0 /* Not intentionally OFF */ AND qso.readonly_reason <> 8 /* Ignore AG secondaries */ @@ -4502,7 +4447,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SELECT check_id = 7012, - priority = 50, /* Medium priority */ + priority = 40, /* Low: tuning recommendation */ category = N''Database Configuration'', finding = N''Query Store Suboptimal Configuration'', database_name = @current_database_name, @@ -4525,7 +4470,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. CONVERT(nvarchar(20), qso.max_plans_per_query) + ''. This may cause relevant plans to be purged prematurely.'' END, - url = N''https://erikdarling.com/sp_PerfCheck#QueryStoreHealth'' + url = N''https://erikdarling.com/sp_perfcheck/#QueryStoreHealth'' FROM ' + QUOTENAME(@current_database_name) + N'.sys.database_query_store_options AS qso WHERE qso.actual_state = 2 /* Query Store is ON */ AND @@ -4714,7 +4659,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ) SELECT check_id = 7020, - priority = 60, /* Informational priority */ + priority = 50, /* Informational: non-default config */ category = N'Database Configuration', finding = N'Non-Default Database Scoped Configuration', database_name = dsc.database_name, @@ -4732,7 +4677,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ELSE N'' END + N'. ', - url = N'https://erikdarling.com/sp_PerfCheck#DSC' + url = N'https://erikdarling.com/sp_perfcheck/#DSC' FROM #database_scoped_configs AS dsc WHERE dsc.database_id = @current_database_id AND dsc.is_value_default = 0; @@ -4764,7 +4709,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ) SELECT check_id = 7007, - priority = 60, /* Informational priority */ + priority = 50, /* Informational */ category = N'Database Configuration', finding = N'Non-Default Target Recovery Time', database_name = d.name, @@ -4772,7 +4717,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. N'Database target recovery time is ' + CONVERT(nvarchar(20), d.target_recovery_time_in_seconds) + N' seconds, which differs from the default of 60 seconds. This affects checkpoint frequency and recovery time.', - url = N'https://erikdarling.com/sp_PerfCheck#RecoveryTime' + url = N'https://erikdarling.com/sp_perfcheck/#RecoveryTime' FROM #databases AS d WHERE d.database_id = @current_database_id AND d.target_recovery_time_in_seconds <> 60; @@ -4791,7 +4736,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ) SELECT check_id = 7008, - priority = 50, /* Medium priority */ + priority = 30, /* Medium: data loss risk on crash */ category = N'Database Configuration', finding = N'Delayed Durability: ' + d.delayed_durability_desc, database_name = d.name, @@ -4799,7 +4744,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. N'Database uses ' + d.delayed_durability_desc + N' durability mode. This can improve performance but increases the risk of data loss during a server failure.', - url = N'https://erikdarling.com/sp_PerfCheck#TransactionDurability' + url = N'https://erikdarling.com/sp_perfcheck/#TransactionDurability' FROM #databases AS d WHERE d.database_id = @current_database_id AND d.delayed_durability_desc <> N'DISABLED'; @@ -4818,14 +4763,14 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ) SELECT check_id = 7009, - priority = 50, /* Medium priority */ + priority = 40, /* Low: recommendation */ category = N'Database Configuration', finding = N'Accelerated Database Recovery Not Enabled With Snapshot Isolation', database_name = d.name, details = N'Database has Snapshot Isolation or RCSI enabled but Accelerated Database Recovery (ADR) is disabled. ' + N'ADR can significantly improve performance with these isolation levels by reducing version store cleanup overhead.', - url = N'https://erikdarling.com/sp_PerfCheck#ADR' + url = N'https://erikdarling.com/sp_perfcheck/#ADR' FROM #databases AS d WHERE d.database_id = @current_database_id AND d.is_accelerated_database_recovery_on = 0 @@ -4849,14 +4794,14 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ) SELECT check_id = 7010, - priority = 60, /* Informational priority */ + priority = 50, /* Informational */ category = N'Database Configuration', finding = N'Ledger Feature Enabled', database_name = d.name, details = N'Database has the ledger feature enabled, which adds blockchain-like capabilities but may impact performance due to additional overhead for maintaining cryptographic verification.', - url = N'https://erikdarling.com/sp_PerfCheck#Ledger' + url = N'https://erikdarling.com/sp_perfcheck/#Ledger' FROM #databases AS d WHERE d.database_id = @current_database_id AND d.is_ledger_on = 1; @@ -4869,7 +4814,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SELECT check_id = 7101, - priority = 40, /* Medium-high priority */ + priority = 40, /* Low: best practice */ category = N''Database Files'', finding = N''Percentage Auto-Growth Setting on Data File'', database_name = @current_database_name, @@ -4882,7 +4827,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. '' GB. This can lead to increasingly larger growth events as the file grows, potentially causing larger file sizes than intended. Even with instant file initialization enabled, consider using a fixed size instead for more predictable growth.'', - url = N''https://erikdarling.com/sp_PerfCheck#DataFileGrowth'' + url = N''https://erikdarling.com/sp_perfcheck/#DataFileGrowth'' FROM ' + QUOTENAME(@current_database_name) + N'.sys.database_files AS mf WHERE mf.is_percent_growth = 1 AND mf.type_desc = N''ROWS'';'; @@ -4915,7 +4860,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SELECT check_id = 7102, - priority = 30, /* High priority */ + priority = 30, /* Medium: log file percent growth */ category = N''Database Files'', finding = N''Percentage Auto-Growth Setting on Log File'', database_name = @current_database_name, @@ -4923,10 +4868,12 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. details = ''Transaction log file is using percentage growth setting ('' + CONVERT(nvarchar(20), mf.growth) + - ''%). This can lead to increasingly larger growth events and significant stalls + ''%). Current file size is '' + + CONVERT(nvarchar(20), CONVERT(decimal(18, 2), mf.size * 8.0 / 1024 / 1024)) + + '' GB. This can lead to increasingly larger growth events and significant stalls as log files must be zeroed out during auto-growth operations. Always use fixed size growth for log files.'', - url = N''https://erikdarling.com/sp_PerfCheck#LogFileGrowth'' + url = N''https://erikdarling.com/sp_perfcheck/#LogFileGrowth'' FROM ' + QUOTENAME(@current_database_name) + N'.sys.database_files AS mf WHERE mf.is_percent_growth = 1 AND mf.type_desc = N''LOG'';'; @@ -4963,7 +4910,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SELECT check_id = 7103, - priority = 40, /* Medium-high priority */ + priority = 40, /* Low: best practice for SQL 2022+ */ category = N''Database Files'', finding = N''Non-Optimal Log File Growth Increment'', database_name = @current_database_name, @@ -4973,7 +4920,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. CONVERT(nvarchar(20), CONVERT(decimal(18, 2), mf.growth * 8.0 / 1024)) + '' MB. '' + ''On SQL Server 2022, Azure SQL DB, or Azure MI, transaction logs can use instant file initialization when set to exactly 64 MB. '' + ''Consider changing the growth increment to 64 MB for improved performance.'', - url = N''https://erikdarling.com/sp_PerfCheck#LogGrowthSize'' + url = N''https://erikdarling.com/sp_perfcheck/#LogGrowthSize'' FROM ' + QUOTENAME(@current_database_name) + N'.sys.database_files AS mf WHERE mf.is_percent_growth = 0 AND mf.type_desc = N''LOG'' @@ -5008,7 +4955,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SELECT check_id = 7104, - priority = 40, /* Medium-high priority */ + priority = 40, /* Low: very large growth increment */ category = N''Database Files'', finding = N''Extremely Large Auto-Growth Setting'', database_name = @current_database_name, @@ -5027,7 +4974,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. WHEN mf.type_desc = N''LOG'' THEN N''This can cause significant stalls as log files must be zeroed out during growth operations.'' END, - url = N''https://erikdarling.com/sp_PerfCheck#LargeGrowth'' + url = N''https://erikdarling.com/sp_perfcheck/#LargeGrowth'' FROM ' + QUOTENAME(@current_database_name) + N'.sys.database_files AS mf WHERE mf.is_percent_growth = 0 AND mf.growth * CONVERT(decimal(18, 2), 8.0) / @@ -5104,6 +5051,15 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SELECT r.check_id, r.priority, + priority_label = + CASE r.priority + WHEN 10 THEN N'Critical' + WHEN 20 THEN N'High' + WHEN 30 THEN N'Medium' + WHEN 40 THEN N'Low' + WHEN 50 THEN N'Informational' + ELSE N'Unknown' + END, r.category, r.finding, r.database_name, diff --git a/sp_PressureDetector/README.md b/sp_PressureDetector/README.md index 3d3b94ff..c06c9f45 100644 --- a/sp_PressureDetector/README.md +++ b/sp_PressureDetector/README.md @@ -34,6 +34,7 @@ All you need to do is hit F5 to get information about: | @log_schema_name | sysname | schema to store logging tables | valid schema name | NULL | | @log_table_name_prefix | sysname | prefix for all logging tables | valid table name prefix | 'PressureDetector' | | @log_retention_days | integer | Number of days to keep logs, 0 = keep indefinitely | integer | 30 | +| @troubleshoot_blocking | bit | show blocking chains instead of pressure analysis | 0 or 1 | 0 | | @help | bit | how you got here | 0 or 1 | 0 | | @debug | bit | prints dynamic sql, displays parameter and variable values, and table contents | 0 or 1 | 0 | | @version | varchar | OUTPUT; for support | none | none; OUTPUT | diff --git a/sp_QuickieStore/README.md b/sp_QuickieStore/README.md index 0e3532e1..2e87c991 100644 --- a/sp_QuickieStore/README.md +++ b/sp_QuickieStore/README.md @@ -28,7 +28,7 @@ Use the `@expert_mode` parameter to return additional details. | parameter_name | data_type | description | valid_inputs | defaults | |-----------------------------------------|----------------|---------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------| | @database_name | sysname | the name of the database you want to look at query store in | a database name with query store enabled | NULL; current database name if NULL | -| @sort_order | varchar | the runtime metric you want to prioritize results by | cpu, logical reads, physical reads, writes, duration, memory, tempdb, executions, recent, plan count by hashes, cpu waits, lock waits, locks waits, latch waits, latches waits, buffer latch waits, buffer latches waits, buffer io waits, log waits, log io waits, network waits, network io waits, parallel waits, parallelism waits, memory waits, total waits, rows | cpu | +| @sort_order | varchar | the runtime metric you want to prioritize results by | cpu, logical reads, physical reads, writes, duration, memory, tempdb, executions, recent, plan count by hashes, cpu waits, lock waits, locks waits, latch waits, latches waits, buffer latch waits, buffer latches waits, buffer io waits, log waits, log io waits, network waits, network io waits, parallel waits, parallelism waits, memory waits, total waits, rows, total cpu, total logical reads, total physical reads, total writes, total duration, total memory, total tempdb, total rows (avg prefix also accepted, e.g. "avg cpu") | cpu | | @top | bigint | the number of queries you want to pull back | a positive integer between 1 and 9,223,372,036,854,775,807 | 10 | | @start_date | datetimeoffset | the begin date of your search, will be converted to UTC internally | January 1, 1753, through December 31, 9999 | the last seven days | | @end_date | datetimeoffset | the end date of your search, will be converted to UTC internally | January 1, 1753, through December 31, 9999 | NULL | @@ -63,8 +63,8 @@ Use the `@expert_mode` parameter to return additional details. | @hide_help_table | bit | hides the "bottom table" that shows help and support information | 0 or 1 | 0 | | @format_output | bit | returns numbers formatted with commas and most decimals rounded away | 0 or 1 | 1 | | @get_all_databases | bit | looks for query store enabled user databases and returns combined results from all of them | 0 or 1 | 0 | -| @include_databases | nvarchar(4000) | comma-separated list of databases to include (only when @get_all_databases = 1) | a string; comma separated database names | NULL | -| @exclude_databases | nvarchar(4000) | comma-separated list of databases to exclude (only when @get_all_databases = 1) | a string; comma separated database names | NULL | +| @include_databases | nvarchar(MAX) | comma-separated list of databases to include (only when @get_all_databases = 1) | a string; comma separated database names | NULL | +| @exclude_databases | nvarchar(MAX) | comma-separated list of databases to exclude (only when @get_all_databases = 1) | a string; comma separated database names | NULL | | @workdays | bit | use this to filter out weekends and after-hours queries | 0 or 1 | 0 | | @work_start | time | use this to set a specific start of your work days | a time like 8am, 9am or something | 9am | | @work_end | time | use this to set a specific end of your work days | a time like 5pm, 6pm or something | 5pm |