From 182995954716eb3e149548cb90402da69e7201c1 Mon Sep 17 00:00:00 2001 From: Brent Ozar Date: Thu, 15 Jun 2023 14:40:52 -0700 Subject: [PATCH 01/30] 2019 CU21 and 2022 CU5 --- SqlServerVersions.sql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/SqlServerVersions.sql b/SqlServerVersions.sql index db33a792..60c0b31e 100644 --- a/SqlServerVersions.sql +++ b/SqlServerVersions.sql @@ -41,12 +41,14 @@ DELETE FROM dbo.SqlServerVersions; INSERT INTO dbo.SqlServerVersions (MajorVersionNumber, MinorVersionNumber, Branch, [Url], ReleaseDate, MainstreamSupportEndDate, ExtendedSupportEndDate, MajorVersionName, MinorVersionName) VALUES + (16, 4045, 'CU5', 'https://support.microsoft.com/en-us/help/5026806', '2023-06-15', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'Cumulative Update 5'), (16, 4035, 'CU4', 'https://support.microsoft.com/en-us/help/5026717', '2023-05-11', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'Cumulative Update 4'), (16, 4025, 'CU3', 'https://support.microsoft.com/en-us/help/5024396', '2023-04-13', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'Cumulative Update 3'), (16, 4015, 'CU2', 'https://support.microsoft.com/en-us/help/5023127', '2023-03-15', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'Cumulative Update 2'), (16, 4003, 'CU1', 'https://support.microsoft.com/en-us/help/5022375', '2023-02-16', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'Cumulative Update 1'), (16, 1050, 'RTM GDR', 'https://support.microsoft.com/kb/5021522', '2023-02-14', '2028-01-11', '2033-01-11', 'SQL Server 2022 GDR', 'RTM'), (16, 1000, 'RTM', '', '2022-11-15', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'RTM'), + (15, 4316, 'CU21', 'https://support.microsoft.com/kb/5025808', '2023-06-15', '2025-01-07', '2030-01-08', 'SQL Server 2019', 'Cumulative Update 21'), (15, 4312, 'CU20', 'https://support.microsoft.com/kb/5024276', '2023-04-13', '2025-01-07', '2030-01-08', 'SQL Server 2019', 'Cumulative Update 20'), (15, 4298, 'CU19', 'https://support.microsoft.com/kb/5023049', '2023-02-16', '2025-01-07', '2030-01-08', 'SQL Server 2019', 'Cumulative Update 19'), (15, 4280, 'CU18 GDR', 'https://support.microsoft.com/kb/5021124', '2023-02-14', '2025-01-07', '2030-01-08', 'SQL Server 2019', 'Cumulative Update 18 GDR'), From 0e71711d735cae4b788ba67aaceffbf1ffbb06e3 Mon Sep 17 00:00:00 2001 From: sqlslinger Date: Mon, 19 Jun 2023 13:54:52 -0400 Subject: [PATCH 02/30] Add @KeepCdc parameter Add the KEEP_CDC option to the RESTORE DATABASE ... WITH RECOVERY command. --- sp_DatabaseRestore.sql | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/sp_DatabaseRestore.sql b/sp_DatabaseRestore.sql index 0442cae9..068d6196 100755 --- a/sp_DatabaseRestore.sql +++ b/sp_DatabaseRestore.sql @@ -31,6 +31,7 @@ ALTER PROCEDURE [dbo].[sp_DatabaseRestore] @DatabaseOwner sysname = NULL, @SetTrustworthyON BIT = 0, @FixOrphanUsers BIT = 0, + @KeepCdc BIT = 0, @Execute CHAR(1) = Y, @FileExtensionDiff NVARCHAR(128) = NULL, @Debug INT = 0, @@ -1490,13 +1491,18 @@ END -- Put database in a useable state IF @RunRecovery = 1 BEGIN - SET @sql = N'RESTORE DATABASE ' + @RestoreDatabaseName + N' WITH RECOVERY' + NCHAR(13); + SET @sql = N'RESTORE DATABASE ' + @RestoreDatabaseName + N' WITH RECOVERY'; + + IF @KeepCdc = 1 + SET @sql = @sql + N', KEEP_CDC'; - IF @Debug = 1 OR @Execute = 'N' - BEGIN - IF @sql IS NULL PRINT '@sql is NULL for RESTORE DATABASE: @RestoreDatabaseName'; - PRINT @sql; - END; + SET @sql = @sql + NCHAR(13); + + IF @Debug = 1 OR @Execute = 'N' + BEGIN + IF @sql IS NULL PRINT '@sql is NULL for RESTORE DATABASE: @RestoreDatabaseName'; + PRINT @sql; + END; IF @Debug IN (0, 1) AND @Execute = 'Y' EXECUTE @sql = [dbo].[CommandExecute] @DatabaseContext=N'master', @Command = @sql, @CommandType = 'RECOVER DATABASE', @Mode = 1, @DatabaseName = @UnquotedRestoreDatabaseName, @LogToTable = 'Y', @Execute = 'Y'; From 8335aa48a45e52fa5db3b94291f25cc6e721d394 Mon Sep 17 00:00:00 2001 From: Brent Ozar Date: Tue, 27 Jun 2023 12:08:32 -0700 Subject: [PATCH 03/30] #3294 sp_BlitzIndex columnstore Add columns from sys.dm_db_column_store_row_group_physical_stats. Working on #3294. --- sp_BlitzIndex.sql | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/sp_BlitzIndex.sql b/sp_BlitzIndex.sql index 6d7cd7b6..1934f794 100644 --- a/sp_BlitzIndex.sql +++ b/sp_BlitzIndex.sql @@ -2958,7 +2958,8 @@ BEGIN + CASE WHEN @ShowPartitionRanges = 1 THEN N' COALESCE(range_start_op + '' '' + range_start + '' '', '''') + COALESCE(range_end_op + '' '' + range_end, '''') AS partition_range, ' ELSE N' ' END + N' row_group_id, total_rows, deleted_rows, ' + @ColumnList - + CASE WHEN @ShowPartitionRanges = 1 THEN N' + + CASE WHEN @ShowPartitionRanges = 1 THEN N' , + state_desc, trim_reason_desc, transition_to_compressed_state_desc, has_vertipaq_optimization FROM ( SELECT column_name, partition_number, row_group_id, total_rows, deleted_rows, details, range_start_op, @@ -2968,9 +2969,11 @@ BEGIN range_end_op, CASE WHEN format_type IS NULL THEN CAST(range_end_value AS NVARCHAR(4000)) - ELSE CONVERT(NVARCHAR(4000), range_end_value, format_type) END range_end' ELSE N' ' END + N' + ELSE CONVERT(NVARCHAR(4000), range_end_value, format_type) END range_end' ELSE N' ' END + N', + state_desc, trim_reason_desc, transition_to_compressed_state_desc, has_vertipaq_optimization FROM ( SELECT c.name AS column_name, p.partition_number, rg.row_group_id, rg.total_rows, rg.deleted_rows, + phys.state_desc, phys.trim_reason_desc, phys.transition_to_compressed_state_desc, phys.has_vertipaq_optimization, details = CAST(seg.min_data_id AS VARCHAR(20)) + '' to '' + CAST(seg.max_data_id AS VARCHAR(20)) + '', '' + CAST(CAST((seg.on_disk_size / 1024.0 / 1024) AS DECIMAL(18,0)) AS VARCHAR(20)) + '' MB''' + CASE WHEN @ShowPartitionRanges = 1 THEN N', CASE @@ -2985,7 +2988,8 @@ BEGIN FROM ' + QUOTENAME(@DatabaseName) + N'.sys.column_store_row_groups rg INNER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.columns c ON rg.object_id = c.object_id INNER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.partitions p ON rg.object_id = p.object_id AND rg.partition_number = p.partition_number - INNER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.index_columns ic on ic.column_id = c.column_id AND ic.object_id = c.object_id AND ic.index_id = p.index_id ' + CASE WHEN @ShowPartitionRanges = 1 THEN N' + INNER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.index_columns ic on ic.column_id = c.column_id AND ic.object_id = c.object_id AND ic.index_id = p.index_id + LEFT OUTER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.dm_db_column_store_row_group_physical_stats phys ON rg.row_group_id = phys.row_group_id AND rg.object_id = phys.object_id AND rg.partition_number = phys.partition_number AND p.index_id = phys.index_id ' + CASE WHEN @ShowPartitionRanges = 1 THEN N' LEFT OUTER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.indexes i ON i.object_id = rg.object_id AND i.index_id = rg.index_id LEFT OUTER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.partition_schemes ps ON ps.data_space_id = i.data_space_id LEFT OUTER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.partition_functions pf ON pf.function_id = ps.function_id From ff8a0f4869a3f040ea3741d134b877288a9b5ad7 Mon Sep 17 00:00:00 2001 From: Erik Darling <2136037+erikdarlingdata@users.noreply.github.com> Date: Wed, 28 Jun 2023 18:28:49 -0400 Subject: [PATCH 04/30] Get plans from cache Closes #3293 Adds a result set that goes out to the plan cache to look for query plans for queries involved in deadlocks. --- sp_BlitzLock.sql | 248 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 202 insertions(+), 46 deletions(-) diff --git a/sp_BlitzLock.sql b/sp_BlitzLock.sql index 9cfbc1e4..51984cfa 100644 --- a/sp_BlitzLock.sql +++ b/sp_BlitzLock.sql @@ -873,9 +873,9 @@ BEGIN SET @d = CONVERT(varchar(40), GETDATE(), 109); RAISERROR('Inserting to #deadlock_data for event file data', 0, 1) WITH NOWAIT; - IF @Debug = 1 BEGIN SET STATISTICS XML ON; END; + IF @Debug = 1 BEGIN SET STATISTICS XML ON; END; - INSERT + INSERT #deadlock_data WITH(TABLOCKX) ( deadlock_xml @@ -897,9 +897,9 @@ BEGIN AND e.x.exist('@timestamp[. < sql:variable("@EndDate")]') = 1 OPTION(RECOMPILE); - IF @Debug = 1 BEGIN SET STATISTICS XML OFF; END; + IF @Debug = 1 BEGIN SET STATISTICS XML OFF; END; - SET @d = CONVERT(varchar(40), GETDATE(), 109); + SET @d = CONVERT(varchar(40), GETDATE(), 109); RAISERROR('Finished at %s', 0, 1, @d) WITH NOWAIT; END; @@ -1116,6 +1116,7 @@ BEGIN FROM #deadlock_process AS dp CROSS APPLY dp.process_xml.nodes('//executionStack/frame') AS ca(dp) WHERE (ca.dp.exist('@procname[. = sql:variable("@StoredProcName")]') = 1 OR @StoredProcName IS NULL) + AND ca.dp.exist('@sqlhandle[ .= "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"]') = 0 OPTION(RECOMPILE); SET @d = CONVERT(varchar(40), GETDATE(), 109); @@ -1584,13 +1585,19 @@ BEGIN 32 ), step_id = - SUBSTRING - ( - dp.client_app, - CHARINDEX(N': Step ', dp.client_app) + LEN(N': Step '), - CHARINDEX(N')', dp.client_app, CHARINDEX(N': Step ', dp.client_app)) - - (CHARINDEX(N': Step ', dp.client_app) + LEN(N': Step ')) - ) + CASE + WHEN CHARINDEX(N': Step ', dp.client_app) > 0 + AND CHARINDEX(N')', dp.client_app, CHARINDEX(N': Step ', dp.client_app)) > 0 + THEN + SUBSTRING + ( + dp.client_app, + CHARINDEX(N': Step ', dp.client_app) + LEN(N': Step '), + CHARINDEX(N')', dp.client_app, CHARINDEX(N': Step ', dp.client_app)) - + (CHARINDEX(N': Step ', dp.client_app) + LEN(N': Step ')) + ) + ELSE dp.client_app + END FROM #deadlock_process AS dp WHERE dp.client_app LIKE N'SQLAgent - %' AND dp.client_app <> N'SQLAgent - Initial Boot Probe' @@ -2211,9 +2218,9 @@ BEGIN deadlock_stack AS ( SELECT DISTINCT - ds.id, - ds.proc_name, - ds.event_date, + ds.id, + ds.event_date, + ds.proc_name, database_name = PARSENAME(ds.proc_name, 3), schema_name = @@ -2247,8 +2254,8 @@ BEGIN PARSENAME(ds.proc_name, 3), PARSENAME(ds.proc_name, 2), PARSENAME(ds.proc_name, 1), - ds.id, ds.proc_name, + ds.id, ds.event_date ) INSERT @@ -2280,6 +2287,7 @@ BEGIN AND (dow.event_date >= @StartDate OR @StartDate IS NULL) AND (dow.event_date < @EndDate OR @EndDate IS NULL) AND (dow.object_name = @StoredProcName OR @StoredProcName IS NULL) + AND ds.proc_name NOT LIKE 'Unknown%' OPTION(RECOMPILE); RAISERROR('Finished at %s', 0, 1, @d) WITH NOWAIT; @@ -2470,19 +2478,19 @@ BEGIN ) ), wait_time_hms = - /*the more wait time you rack up the less accurate this gets, + /*the more wait time you rack up the less accurate this gets, it's either that or erroring out*/ - CASE - WHEN + CASE + WHEN SUM ( CONVERT ( - bigint, + bigint, dp.wait_time ) )/1000 > 2147483647 - THEN + THEN CONVERT ( nvarchar(30), @@ -2495,7 +2503,7 @@ BEGIN ( CONVERT ( - bigint, + bigint, dp.wait_time ) ) @@ -2506,16 +2514,16 @@ BEGIN ), 14 ) - WHEN + WHEN SUM ( CONVERT ( - bigint, + bigint, dp.wait_time ) ) BETWEEN 2147483648 AND 2147483647000 - THEN + THEN CONVERT ( nvarchar(30), @@ -2528,7 +2536,7 @@ BEGIN ( CONVERT ( - bigint, + bigint, dp.wait_time ) ) @@ -2677,19 +2685,19 @@ BEGIN ) ) + N' ' + - /*the more wait time you rack up the less accurate this gets, + /*the more wait time you rack up the less accurate this gets, it's either that or erroring out*/ - CASE - WHEN + CASE + WHEN SUM ( CONVERT ( - bigint, + bigint, wt.total_wait_time_ms ) )/1000 > 2147483647 - THEN + THEN CONVERT ( nvarchar(30), @@ -2702,7 +2710,7 @@ BEGIN ( CONVERT ( - bigint, + bigint, wt.total_wait_time_ms ) ) @@ -2713,16 +2721,16 @@ BEGIN ), 14 ) - WHEN + WHEN SUM ( CONVERT ( - bigint, + bigint, wt.total_wait_time_ms ) ) BETWEEN 2147483648 AND 2147483647000 - THEN + THEN CONVERT ( nvarchar(30), @@ -2735,7 +2743,7 @@ BEGIN ( CONVERT ( - bigint, + bigint, wt.total_wait_time_ms ) ) @@ -3345,7 +3353,8 @@ BEGIN d.waiter_waiting_to_close, /*end parallel deadlock columns*/ d.deadlock_graph, - d.is_victim + d.is_victim, + d.id INTO #deadlock_results FROM #deadlocks AS d; @@ -3548,26 +3557,173 @@ BEGIN DROP SYNONYM DeadlockFindings; /*done with inserting.*/ END; ELSE /*Output to database is not set output to client app*/ - BEGIN - SET @d = CONVERT(varchar(40), GETDATE(), 109); + BEGIN + SET @d = CONVERT(varchar(40), GETDATE(), 109); RAISERROR('Results to client %s', 0, 1, @d) WITH NOWAIT; - + IF @Debug = 1 BEGIN SET STATISTICS XML ON; END; - + EXEC sys.sp_executesql @deadlock_result; - + IF @Debug = 1 BEGIN SET STATISTICS XML OFF; PRINT @deadlock_result; END; - + + RAISERROR('Finished at %s', 0, 1, @d) WITH NOWAIT; + + SET @d = CONVERT(varchar(40), GETDATE(), 109); + RAISERROR('Getting available execution plans for deadlocks %s', 0, 1, @d) WITH NOWAIT; + + SELECT DISTINCT + ds.proc_name, + sql_handle = + CONVERT(varbinary(64), ds.sql_handle, 1), + dow.database_name, + dow.object_name, + query_xml = + CONVERT(nvarchar(MAX), dr.query_xml) + INTO #available_plans + FROM #deadlock_stack AS ds + JOIN #deadlock_owner_waiter AS dow + ON dow.owner_id = ds.id + AND dow.event_date = ds.event_date + JOIN #deadlock_results AS dr + ON dr.id = ds.id + AND dr.event_date = ds.event_date + OPTION(RECOMPILE); + + SELECT + ap.database_name, + query_text = + TRY_CAST(ap.query_xml AS xml), + ap.query_plan, + ap.creation_time, + ap.last_execution_time, + ap.execution_count, + ap.executions_per_second, + ap.total_worker_time_ms, + ap.avg_worker_time_ms, + ap.total_elapsed_time_ms, + ap.avg_elapsed_time, + ap.total_logical_reads_mb, + ap.total_physical_reads_mb, + ap.min_num_physical_reads_mb, + ap.max_num_physical_reads_mb, + ap.total_logical_writes_mb, + ap.min_grant_mb, + ap.max_grant_mb, + ap.min_used_grant_mb, + ap.max_used_grant_mb, + ap.min_spills_mb, + ap.max_spills_mb, + ap.min_reserved_threads, + ap.max_reserved_threads, + ap.min_used_threads, + ap.max_used_threads, + ap.total_rows, + ap.sql_handle, + ap.statement_start_offset, + ap.statement_end_offset + FROM + ( + SELECT + *, + n = + ROW_NUMBER() OVER + ( + PARTITION BY + ap.sql_handle + ORDER BY + ap.sql_handle + ) + FROM #available_plans AS ap + OUTER APPLY + ( + SELECT TOP (1) + deqs.statement_start_offset, + deqs.statement_end_offset, + deqs.creation_time, + deqs.last_execution_time, + deqs.execution_count, + total_worker_time_ms = + deqs.total_worker_time / 1000., + avg_worker_time_ms = + CONVERT(decimal(38, 6), deqs.total_worker_time / 1000. / deqs.execution_count), + total_elapsed_time_ms = + deqs.total_elapsed_time / 1000., + avg_elapsed_time = + CONVERT(decimal(38, 6), deqs.total_elapsed_time / 1000. / deqs.execution_count), + executions_per_second = + ISNULL + ( + execution_count / + NULLIF + ( + DATEDIFF + ( + SECOND, + deqs.creation_time, + deqs.last_execution_time + ), + 0 + ), + 0 + ), + total_physical_reads_mb = + deqs.total_physical_reads * 8. / 1024., + total_logical_writes_mb = + deqs.total_logical_writes * 8. / 1024., + total_logical_reads_mb = + deqs.total_logical_reads * 8. / 1024., + min_num_physical_reads_mb = + deqs.min_num_physical_reads * 8. / 1024., + max_num_physical_reads_mb = + deqs.max_num_physical_reads * 8. / 1024., + min_grant_mb = + deqs.min_grant_kb * 8. / 1024., + max_grant_mb = + deqs.max_grant_kb * 8. / 1024., + min_used_grant_mb = + deqs.min_used_grant_kb * 8. / 1024., + max_used_grant_mb = + deqs.max_used_grant_kb * 8. / 1024., + min_spills_mb = + deqs.min_spills * 8. / 1024., + max_spills_mb = + deqs.max_spills * 8. / 1024., + deqs.min_reserved_threads, + deqs.max_reserved_threads, + deqs.min_used_threads, + deqs.max_used_threads, + deqs.total_rows, + query_plan = + TRY_CAST(deps.query_plan AS xml) + FROM sys.dm_exec_query_stats AS deqs + OUTER APPLY sys.dm_exec_text_query_plan + ( + deqs.plan_handle, + deqs.statement_start_offset, + deqs.statement_end_offset + ) AS deps + WHERE deqs.sql_handle = ap.sql_handle + ORDER BY + deqs.last_execution_time DESC + ) AS c + ) AS ap + WHERE ap.query_plan IS NOT NULL + AND ap.n = 1 + ORDER BY + ap.last_execution_time DESC + OPTION(RECOMPILE); + RAISERROR('Finished at %s', 0, 1, @d) WITH NOWAIT; - + SET @d = CONVERT(varchar(40), GETDATE(), 109); RAISERROR('Returning findings %s', 0, 1, @d) WITH NOWAIT; - + SELECT df.check_id, df.database_name, @@ -3577,11 +3733,11 @@ BEGIN FROM #deadlock_findings AS df ORDER BY df.check_id OPTION(RECOMPILE); - + SET @d = CONVERT(varchar(40), GETDATE(), 109); RAISERROR('Finished at %s', 0, 1, @d) WITH NOWAIT; END; /*done with output to client app.*/ - END; + END; IF @Debug = 1 BEGIN From 91f4d92303e0f074fa61d657d28dfbc40697f474 Mon Sep 17 00:00:00 2001 From: "marlon.ambion" Date: Thu, 29 Jun 2023 14:31:55 +1200 Subject: [PATCH 05/30] #3291 FIX: sp_BlitzCache fails when executed on instances with readable secondary replicas Change: Add internal resource database "32767" in the ReadableDB list for exclusion --- sp_BlitzCache.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/sp_BlitzCache.sql b/sp_BlitzCache.sql index 49ad85cd..11600c64 100644 --- a/sp_BlitzCache.sql +++ b/sp_BlitzCache.sql @@ -1579,6 +1579,7 @@ BEGIN RAISERROR('Checking for Read intent databases to exclude',0,0) WITH NOWAIT; EXEC('INSERT INTO #ReadableDBs (database_id) SELECT DBs.database_id FROM sys.databases DBs INNER JOIN sys.availability_replicas Replicas ON DBs.replica_id = Replicas.replica_id WHERE replica_server_name NOT IN (SELECT DISTINCT primary_replica FROM sys.dm_hadr_availability_group_states States) AND Replicas.secondary_role_allow_connections_desc = ''READ_ONLY'' AND replica_server_name = @@SERVERNAME OPTION (RECOMPILE);'); + EXEC('INSERT INTO #ReadableDBs VALUES (32767) ;'); -- Exclude internal resource database as well END RAISERROR(N'Checking plan cache age', 0, 1) WITH NOWAIT; From ac6bb26de09f3116a0d53aa260d32702c486d954 Mon Sep 17 00:00:00 2001 From: Erik Darling <2136037+erikdarlingdata@users.noreply.github.com> Date: Thu, 29 Jun 2023 21:29:52 -0400 Subject: [PATCH 06/30] Add better sorting Closes #3298 --- sp_BlitzLock.sql | 118 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 88 insertions(+), 30 deletions(-) diff --git a/sp_BlitzLock.sql b/sp_BlitzLock.sql index 51984cfa..c2c1480e 100644 --- a/sp_BlitzLock.sql +++ b/sp_BlitzLock.sql @@ -219,7 +219,8 @@ BEGIN database_name nvarchar(256), object_name nvarchar(1000), finding_group nvarchar(100), - finding nvarchar(4000) + finding nvarchar(4000), + sort_order bigint ); /*Set these to some sane defaults if NULLs are passed in*/ @@ -1758,7 +1759,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 1, @@ -1772,7 +1774,10 @@ BEGIN nvarchar(20), COUNT_BIG(DISTINCT dp.event_date) ) + - N' deadlocks.' + N' deadlocks.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT dp.event_date) DESC) FROM #deadlock_process AS dp WHERE 1 = 1 AND (dp.database_name = @DatabaseName OR @DatabaseName IS NULL) @@ -1797,7 +1802,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 2, @@ -1812,7 +1818,10 @@ BEGIN nvarchar(20), COUNT_BIG(DISTINCT dow.event_date) ) + - N' deadlock(s) between read queries and modification queries.' + N' deadlock(s) between read queries and modification queries.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT dow.event_date) DESC) FROM #deadlock_owner_waiter AS dow WHERE 1 = 1 AND dow.lock_mode IN @@ -1851,7 +1860,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 3, @@ -1870,7 +1880,10 @@ BEGIN nvarchar(20), COUNT_BIG(DISTINCT dow.event_date) ) + - N' deadlock(s).' + N' deadlock(s).', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT dow.event_date) DESC) FROM #deadlock_owner_waiter AS dow WHERE 1 = 1 AND (dow.database_id = @DatabaseId OR @DatabaseName IS NULL) @@ -1895,7 +1908,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 3, @@ -1909,7 +1923,10 @@ BEGIN nvarchar(20), COUNT_BIG(DISTINCT dow.event_date) ) + - N' deadlock(s).' + N' deadlock(s).', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT dow.event_date) DESC) FROM #deadlock_owner_waiter AS dow WHERE 1 = 1 AND (dow.database_id = @DatabaseId OR @DatabaseName IS NULL) @@ -1940,7 +1957,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 3, @@ -1954,7 +1972,10 @@ BEGIN nvarchar(20), COUNT_BIG(DISTINCT dow.event_date) ) + - N' deadlock(s).' + N' deadlock(s).', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT dow.event_date) DESC) FROM #deadlock_owner_waiter AS dow WHERE 1 = 1 AND (dow.database_id = @DatabaseId OR @DatabaseName IS NULL) @@ -1984,7 +2005,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 4, @@ -1999,7 +2021,10 @@ BEGIN nvarchar(20), COUNT_BIG(DISTINCT dp.event_date) ) + - N' instances of Serializable deadlocks.' + N' instances of Serializable deadlocks.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT dow.event_date) DESC) FROM #deadlock_process AS dp WHERE dp.isolation_level LIKE N'serializable%' AND (dp.database_name = @DatabaseName OR @DatabaseName IS NULL) @@ -2024,7 +2049,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 5, @@ -2038,7 +2064,10 @@ BEGIN nvarchar(20), COUNT_BIG(DISTINCT dp.event_date) ) + - N' instances of Repeatable Read deadlocks.' + N' instances of Repeatable Read deadlocks.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT dow.event_date) DESC) FROM #deadlock_process AS dp WHERE dp.isolation_level LIKE N'repeatable%' AND (dp.database_name = @DatabaseName OR @DatabaseName IS NULL) @@ -2063,7 +2092,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 6, @@ -2096,7 +2126,10 @@ BEGIN dp.host_name, N'UNKNOWN' ) + - N'.' + N'.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT dp.event_date) DESC) FROM #deadlock_process AS dp WHERE 1 = 1 AND (dp.database_name = @DatabaseName OR @DatabaseName IS NULL) @@ -2177,7 +2210,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 7, @@ -2204,7 +2238,10 @@ BEGIN 1, 1, N'' - ) + N' locks.' + ) + N' locks.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY CONVERT(bigint, lt2.lock_count) DESC) FROM lock_types AS lt OPTION(RECOMPILE); @@ -2360,7 +2397,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 9, @@ -2379,7 +2417,10 @@ BEGIN nvarchar(10), COUNT_BIG(DISTINCT ds.id) ) + - N' deadlocks.' + N' deadlocks.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT ds.id) DESC) FROM #deadlock_stack AS ds JOIN #deadlock_process AS dp ON dp.id = ds.id @@ -2568,7 +2609,9 @@ BEGIN ), 14 ) - END + END, + total_waits = + SUM(CONVERT(bigint, dp.wait_time)) FROM #deadlock_owner_waiter AS dow JOIN #deadlock_process AS dp ON (dp.id = dow.owner_id @@ -2593,7 +2636,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 11, @@ -2614,7 +2658,10 @@ BEGIN cs.wait_time_hms, 14 ) + - N' [dd hh:mm:ss:ms] of deadlock wait time.' + N' [dd hh:mm:ss:ms] of deadlock wait time.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY cs.total_waits DESC) FROM chopsuey AS cs WHERE cs.object_name IS NOT NULL OPTION(RECOMPILE); @@ -2662,7 +2709,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 12, @@ -2775,7 +2823,10 @@ BEGIN ), 14 ) END + - N' [dd hh:mm:ss:ms] of deadlock wait time.' + N' [dd hh:mm:ss:ms] of deadlock wait time.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY wt.total_wait_time_ms DESC) FROM wait_time AS wt GROUP BY wt.database_name @@ -2794,7 +2845,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 13, @@ -2809,7 +2861,10 @@ BEGIN finding = N'There have been ' + RTRIM(COUNT_BIG(DISTINCT aj.event_date)) + - N' deadlocks from this Agent Job and Step.' + N' deadlocks from this Agent Job and Step.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT aj.event_date) DESC) FROM #agent_job AS aj GROUP BY DB_NAME(aj.database_id), @@ -2925,7 +2980,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) VALUES ( @@ -3731,7 +3787,9 @@ BEGIN df.finding_group, df.finding FROM #deadlock_findings AS df - ORDER BY df.check_id + ORDER BY + df.check_id, + df.sort_order OPTION(RECOMPILE); SET @d = CONVERT(varchar(40), GETDATE(), 109); From c5405fd274c79fcba5307a4dadaee999db47715c Mon Sep 17 00:00:00 2001 From: Erik Darling <2136037+erikdarlingdata@users.noreply.github.com> Date: Thu, 29 Jun 2023 21:36:30 -0400 Subject: [PATCH 07/30] Quality assurance --- sp_BlitzLock.sql | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/sp_BlitzLock.sql b/sp_BlitzLock.sql index c2c1480e..dafba72a 100644 --- a/sp_BlitzLock.sql +++ b/sp_BlitzLock.sql @@ -2024,7 +2024,7 @@ BEGIN N' instances of Serializable deadlocks.', sort_order = ROW_NUMBER() - OVER (ORDER BY COUNT_BIG(DISTINCT dow.event_date) DESC) + OVER (ORDER BY COUNT_BIG(DISTINCT dp.event_date) DESC) FROM #deadlock_process AS dp WHERE dp.isolation_level LIKE N'serializable%' AND (dp.database_name = @DatabaseName OR @DatabaseName IS NULL) @@ -2067,7 +2067,7 @@ BEGIN N' instances of Repeatable Read deadlocks.', sort_order = ROW_NUMBER() - OVER (ORDER BY COUNT_BIG(DISTINCT dow.event_date) DESC) + OVER (ORDER BY COUNT_BIG(DISTINCT dp.event_date) DESC) FROM #deadlock_process AS dp WHERE dp.isolation_level LIKE N'repeatable%' AND (dp.database_name = @DatabaseName OR @DatabaseName IS NULL) @@ -2241,7 +2241,7 @@ BEGIN ) + N' locks.', sort_order = ROW_NUMBER() - OVER (ORDER BY CONVERT(bigint, lt2.lock_count) DESC) + OVER (ORDER BY CONVERT(bigint, lt.lock_count) DESC) FROM lock_types AS lt OPTION(RECOMPILE); @@ -2826,7 +2826,7 @@ BEGIN N' [dd hh:mm:ss:ms] of deadlock wait time.', sort_order = ROW_NUMBER() - OVER (ORDER BY wt.total_wait_time_ms DESC) + OVER (ORDER BY SUM(CONVERT(bigint, wt.total_wait_time_ms)) DESC) FROM wait_time AS wt GROUP BY wt.database_name @@ -2980,8 +2980,7 @@ BEGIN database_name, object_name, finding_group, - finding, - sort_order + finding ) VALUES ( From 4bb9866786a47a8b04189c029ab8dadfac68f298 Mon Sep 17 00:00:00 2001 From: Brent Ozar Date: Fri, 30 Jun 2023 09:06:41 -0700 Subject: [PATCH 08/30] #3294 sp_BlitzIndex: add dictionary sizes In the columnstore visualization, secondary dictionary only. Working on #3294. --- sp_BlitzIndex.sql | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sp_BlitzIndex.sql b/sp_BlitzIndex.sql index 1934f794..5ad1e33a 100644 --- a/sp_BlitzIndex.sql +++ b/sp_BlitzIndex.sql @@ -2974,7 +2974,7 @@ BEGIN FROM ( SELECT c.name AS column_name, p.partition_number, rg.row_group_id, rg.total_rows, rg.deleted_rows, phys.state_desc, phys.trim_reason_desc, phys.transition_to_compressed_state_desc, phys.has_vertipaq_optimization, - details = CAST(seg.min_data_id AS VARCHAR(20)) + '' to '' + CAST(seg.max_data_id AS VARCHAR(20)) + '', '' + CAST(CAST((seg.on_disk_size / 1024.0 / 1024) AS DECIMAL(18,0)) AS VARCHAR(20)) + '' MB''' + details = CAST(seg.min_data_id AS VARCHAR(20)) + '' to '' + CAST(seg.max_data_id AS VARCHAR(20)) + '', '' + CAST(CAST(((COALESCE(d.on_disk_size,0) + COALESCE(seg.on_disk_size,0)) / 1024.0 / 1024) AS DECIMAL(18,0)) AS VARCHAR(20)) + '' MB''' + CASE WHEN @ShowPartitionRanges = 1 THEN N', CASE WHEN pp.system_type_id IN (40, 41, 42, 43, 58, 61) THEN 126 @@ -2997,6 +2997,7 @@ BEGIN LEFT OUTER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.partition_range_values prvs ON prvs.function_id = pf.function_id AND prvs.boundary_id = p.partition_number - 1 LEFT OUTER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.partition_range_values prve ON prve.function_id = pf.function_id AND prve.boundary_id = p.partition_number ' ELSE N' ' END + N' LEFT OUTER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.column_store_segments seg ON p.partition_id = seg.partition_id AND ic.index_column_id = seg.column_id AND rg.row_group_id = seg.segment_id + LEFT OUTER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.column_store_dictionaries d ON p.hobt_id = d.hobt_id AND c.column_id = d.column_id AND seg.secondary_dictionary_id = d.dictionary_id WHERE rg.object_id = @ObjectID AND rg.state IN (1, 2, 3) AND c.name IN ( ' + @ColumnListWithApostrophes + N')' @@ -3034,6 +3035,9 @@ BEGIN RAISERROR(N'Done visualizing columnstore index contents.', 0,1) WITH NOWAIT; END + IF @ShowColumnstoreOnly = 1 + RETURN; + END; /* IF @TableName IS NOT NULL */ From 3900d6eed9d91246cddaccef10b716d6bd261920 Mon Sep 17 00:00:00 2001 From: Erik Darling <2136037+erikdarlingdata@users.noreply.github.com> Date: Sun, 2 Jul 2023 11:26:56 -0400 Subject: [PATCH 09/30] Add some permissions checks to sp_Blitz MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #3292 This feels like a somewhat naïve set of checks. I don't know a ton about security. It may clash when someone uses `EXECUTE AS` or signs the procedure for execution for lower-privileged users. It's also incomplete at the moment, because I need to round up commands that touch system databases we may not have read permissions in. I fully expect this to get rejected, but it got me error-free runs in a tightly locked down SQL Server environment. If anyone has feedback, I'm happy to take it. --- sp_Blitz.sql | 279 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 230 insertions(+), 49 deletions(-) diff --git a/sp_Blitz.sql b/sp_Blitz.sql index 43657817..7e6eaae9 100644 --- a/sp_Blitz.sql +++ b/sp_Blitz.sql @@ -186,11 +186,128 @@ AS ,@CurrentComponentVersionCheckModeOK BIT ,@canExitLoop BIT ,@frkIsConsistent BIT - ,@NeedToTurnNumericRoundabortBackOn BIT; + ,@NeedToTurnNumericRoundabortBackOn BIT + ,@sa bit = 1 + ,@SUSER_NAME sysname = SUSER_SNAME() + ,@SkipDBCC bit = 0 + ,@SkipTrace bit = 0 + ,@SkipXPRegRead bit = 0 + ,@SkipXPFixedDrives bit = 0 + ,@SkipXPCMDShell bit = 0 + ,@SkipMaster bit = 0 + ,@SkipMSDB bit = 0 + ,@SkipModel bit = 0 + ,@SkipTempDB bit = 0 + ,@SkipValidateLogins bit = 0; + + DECLARE + @db_perms table + ( + database_name sysname, + permission_name sysname + ); + /* End of declarations for First Responder Kit consistency check:*/ ; + /*Starting permissions checks here, but only if we're not a sysadmin*/ + IF + ( + SELECT + sa = + ISNULL + ( + IS_SRVROLEMEMBER(N'sysadmin'), + 0 + ) + ) = 0 + BEGIN + SET @sa = 0; /*Setting this to 0 to skip DBCC COMMANDS*/ + + IF NOT EXISTS + ( + SELECT + 1/0 + FROM sys.fn_my_permissions(NULL, NULL) AS fmp + WHERE fmp.permission_name = N'VIEW SERVER STATE' + ) + BEGIN + RAISERROR('The user %s does not have VIEW SERVER STATE permissions.', 0, 11, @SUSER_NAME) WITH NOWAIT; + RETURN; + END; /*If we don't have this, we can't do anything at all.*/ + + IF NOT EXISTS + ( + SELECT + 1/0 + FROM fn_my_permissions(N'sys.traces', N'OBJECT') AS fmp + WHERE fmp.permission_name = N'ALTER' + ) + BEGIN + SET @SkipTrace = 1; + END; /*We need this permission to execute trace stuff, apparently*/ + + IF NOT EXISTS + ( + SELECT + 1/0 + FROM fn_my_permissions(N'xp_regread', N'OBJECT') AS fmp + WHERE fmp.permission_name = N'EXECUTE' + ) + BEGIN + SET @SkipXPRegRead = 1; + END; /*Need execute on xp_regread*/ + + IF NOT EXISTS + ( + SELECT + 1/0 + FROM fn_my_permissions(N'xp_fixeddrives', N'OBJECT') AS fmp + WHERE fmp.permission_name = N'EXECUTE' + ) + BEGIN + SET @SkipXPFixedDrives = 1; + END; /*Need execute on xp_fixeddrives*/ + + IF NOT EXISTS + ( + SELECT + 1/0 + FROM fn_my_permissions(N'xp_cmdshell', N'OBJECT') AS fmp + WHERE fmp.permission_name = N'EXECUTE' + ) + BEGIN + SET @SkipXPCMDShell = 1; + END; /*Need execute on xp_cmdshell*/ + + IF NOT EXISTS + ( + SELECT + 1/0 + FROM fn_my_permissions(N'sp_validatelogins', N'OBJECT') AS fmp + WHERE fmp.permission_name = N'EXECUTE' + ) + BEGIN + SET @SkipValidateLogins = 1; + END; /*Need execute on sp_validatelogins*/ + + INSERT + @db_perms + ( + database_name, + permission_name + ) + SELECT + database_name = + DB_NAME(d.database_id), + fmp.permission_name + FROM sys.databases AS d + CROSS APPLY fn_my_permissions(d.name, 'DATABASE') AS fmp + WHERE fmp.permission_name = N'SELECT' + AND d.database_id < 5; /*Databases where we don't have read permissions*/ + END; + SET @crlf = NCHAR(13) + NCHAR(10); SET @ResultText = 'sp_Blitz Results: ' + @crlf; @@ -331,6 +448,66 @@ AS OR LOWER(d.name) IN ('dbatools', 'dbadmin', 'dbmaintenance')) OPTION(RECOMPILE); + /*Skip checks for database where we don't have read permissions*/ + INSERT INTO + #SkipChecks + ( + DatabaseName + ) + SELECT + DB_NAME(d.database_id) + FROM sys.databases AS d + WHERE NOT EXISTS + ( + SELECT + 1/0 + FROM @db_perms AS dp + WHERE dp.database_name = DB_NAME(d.database_id) + ); + + /*Skip individial checks where we don't have permissions*/ + INSERT #SkipChecks (DatabaseName, CheckID, ServerName) + SELECT + v.* + FROM (VALUES(NULL, NULL, 29)) AS v (DatabaseName, CheckID, ServerName) /*Looks for user tables in model*/ + WHERE NOT EXISTS (SELECT 1/0 FROM @db_perms AS dp WHERE dp.database_name = 'model'); + + INSERT #SkipChecks (DatabaseName, CheckID, ServerName) + SELECT + v.* + FROM (VALUES(NULL, NULL, 68)) AS v (DatabaseName, CheckID, ServerName) /*DBCC command*/ + WHERE @sa = 0; + + INSERT #SkipChecks (DatabaseName, CheckID, ServerName) + SELECT + v.* + FROM (VALUES(NULL, NULL, 69)) AS v (DatabaseName, CheckID, ServerName) /*DBCC command*/ + WHERE @sa = 0; + + INSERT #SkipChecks (DatabaseName, CheckID, ServerName) + SELECT + v.* + FROM (VALUES(NULL, NULL, 92)) AS v (DatabaseName, CheckID, ServerName) /*xp_fixeddrives*/ + WHERE @SkipXPFixedDrives = 1; + + INSERT #SkipChecks (DatabaseName, CheckID, ServerName) + SELECT + v.* + FROM (VALUES(NULL, NULL, 211)) AS v (DatabaseName, CheckID, ServerName) /*xp_regread*/ + WHERE @SkipXPRegRead = 1; + + INSERT #SkipChecks (DatabaseName, CheckID, ServerName) + SELECT + v.* + FROM (VALUES(NULL, NULL, 212)) AS v (DatabaseName, CheckID, ServerName) /*xp_regread*/ + WHERE @SkipXPCMDShell = 1; + + INSERT #SkipChecks (DatabaseName, CheckID, ServerName) + SELECT + v.* + FROM (VALUES(NULL, NULL, 2301)) AS v (DatabaseName, CheckID, ServerName) /*sp_validatelogins*/ + WHERE @SkipValidateLogins = 1 + IF(OBJECT_ID('tempdb..#InvalidLogins') IS NOT NULL) BEGIN EXEC sp_executesql N'DROP TABLE #InvalidLogins;'; @@ -372,7 +549,8 @@ AS SELECT @IsWindowsOperatingSystem = 1 ; END; - IF NOT EXISTS ( SELECT 1 + + IF NOT EXISTS ( SELECT 1 FROM #SkipChecks WHERE DatabaseName IS NULL AND CheckID = 106 ) AND (select convert(int,value_in_use) from sys.configurations where name = 'default trace enabled' ) = 1 @@ -4158,53 +4336,56 @@ AS /* First, let's check that there aren't any issues with the trace files */ BEGIN TRY - - INSERT INTO #fnTraceGettable - ( TextData , - DatabaseName , - EventClass , - Severity , - StartTime , - EndTime , - Duration , - NTUserName , - NTDomainName , - HostName , - ApplicationName , - LoginName , - DBUserName - ) - SELECT TOP 20000 - CONVERT(NVARCHAR(4000),t.TextData) , - t.DatabaseName , - t.EventClass , - t.Severity , - t.StartTime , - t.EndTime , - t.Duration , - t.NTUserName , - t.NTDomainName , - t.HostName , - t.ApplicationName , - t.LoginName , - t.DBUserName - FROM sys.fn_trace_gettable(@base_tracefilename, DEFAULT) t - WHERE - ( - t.EventClass = 22 - AND t.Severity >= 17 - AND t.StartTime > DATEADD(dd, -30, GETDATE()) - ) - OR - ( - t.EventClass IN (92, 93) - AND t.StartTime > DATEADD(dd, -30, GETDATE()) - AND t.Duration > 15000000 - ) - OR - ( - t.EventClass IN (94, 95, 116) - ) + + IF @SkipTrace = 0 + BEGIN + INSERT INTO #fnTraceGettable + ( TextData , + DatabaseName , + EventClass , + Severity , + StartTime , + EndTime , + Duration , + NTUserName , + NTDomainName , + HostName , + ApplicationName , + LoginName , + DBUserName + ) + SELECT TOP 20000 + CONVERT(NVARCHAR(4000),t.TextData) , + t.DatabaseName , + t.EventClass , + t.Severity , + t.StartTime , + t.EndTime , + t.Duration , + t.NTUserName , + t.NTDomainName , + t.HostName , + t.ApplicationName , + t.LoginName , + t.DBUserName + FROM sys.fn_trace_gettable(@base_tracefilename, DEFAULT) t + WHERE + ( + t.EventClass = 22 + AND t.Severity >= 17 + AND t.StartTime > DATEADD(dd, -30, GETDATE()) + ) + OR + ( + t.EventClass IN (92, 93) + AND t.StartTime > DATEADD(dd, -30, GETDATE()) + AND t.Duration > 15000000 + ) + OR + ( + t.EventClass IN (94, 95, 116) + ) + END; SET @TraceFileIssue = 0 From 0eab78ead50cbd29f8faada7fba11322a2895f76 Mon Sep 17 00:00:00 2001 From: Erik Darling <2136037+erikdarlingdata@users.noreply.github.com> Date: Thu, 6 Jul 2023 13:26:42 -0400 Subject: [PATCH 10/30] Update sp_BlitzLock.sql Removes the `_num` from the physical reads columns, which aren't available in SQL Server 2016. Replaces them with columns that are available with physical reads metrics. --- sp_BlitzLock.sql | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sp_BlitzLock.sql b/sp_BlitzLock.sql index dafba72a..79f1693d 100644 --- a/sp_BlitzLock.sql +++ b/sp_BlitzLock.sql @@ -3665,8 +3665,8 @@ BEGIN ap.avg_elapsed_time, ap.total_logical_reads_mb, ap.total_physical_reads_mb, - ap.min_num_physical_reads_mb, - ap.max_num_physical_reads_mb, + ap.min_physical_reads_mb, + ap.max_physical_reads_mb, ap.total_logical_writes_mb, ap.min_grant_mb, ap.max_grant_mb, @@ -3733,10 +3733,10 @@ BEGIN deqs.total_logical_writes * 8. / 1024., total_logical_reads_mb = deqs.total_logical_reads * 8. / 1024., - min_num_physical_reads_mb = - deqs.min_num_physical_reads * 8. / 1024., - max_num_physical_reads_mb = - deqs.max_num_physical_reads * 8. / 1024., + min_physical_reads_mb = + deqs.min_physical_reads * 8. / 1024., + max_physical_reads_mb = + deqs.max_physical_reads * 8. / 1024., min_grant_mb = deqs.min_grant_kb * 8. / 1024., max_grant_mb = From 731451879fd9c0e47f0da1c5c4bcb3130ef0ada2 Mon Sep 17 00:00:00 2001 From: Brent Ozar Date: Thu, 13 Jul 2023 02:56:01 -0700 Subject: [PATCH 11/30] #3190 sp_DatabaseRestore data types Matching Microsoft's documentation. #3190 --- sp_DatabaseRestore.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sp_DatabaseRestore.sql b/sp_DatabaseRestore.sql index 068d6196..06968cad 100755 --- a/sp_DatabaseRestore.sql +++ b/sp_DatabaseRestore.sql @@ -366,8 +366,8 @@ CREATE TABLE #Headers EncryptorThumbprint VARBINARY(20), EncryptorType NVARCHAR(32), LastValidRestoreTime DATETIME, - TimeZone NVARCHAR(256), - CompressionAlgorithm NVARCHAR(256), + TimeZone NVARCHAR(32), + CompressionAlgorithm NVARCHAR(32), -- -- Seq added to retain order by -- From ca7cffcc9f21ecba39f8ee589a3f8c46c199c611 Mon Sep 17 00:00:00 2001 From: Erik Darling <2136037+erikdarlingdata@users.noreply.github.com> Date: Sat, 15 Jul 2023 11:23:28 -0400 Subject: [PATCH 12/30] Enhanced! XML filtering Closes #3305 --- sp_BlitzLock.sql | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/sp_BlitzLock.sql b/sp_BlitzLock.sql index 79f1693d..1a85d612 100644 --- a/sp_BlitzLock.sql +++ b/sp_BlitzLock.sql @@ -856,8 +856,7 @@ BEGIN OR e.x.exist('@name[ .= "database_xml_deadlock_report"]') = 1 OR e.x.exist('@name[ .= "xml_deadlock_report_filtered"]') = 1 ) - AND e.x.exist('@timestamp[. >= sql:variable("@StartDate")]') = 1 - AND e.x.exist('@timestamp[. < sql:variable("@EndDate")]') = 1 + AND e.x.exist('@timestamp[. >= sql:variable("@StartDate") and .< sql:variable("@EndDate")]') = 1 OPTION(RECOMPILE); SET @d = CONVERT(varchar(40), GETDATE(), 109); @@ -894,8 +893,7 @@ BEGIN OR e.x.exist('@name[ .= "database_xml_deadlock_report"]') = 1 OR e.x.exist('@name[ .= "xml_deadlock_report_filtered"]') = 1 ) - AND e.x.exist('@timestamp[. >= sql:variable("@StartDate")]') = 1 - AND e.x.exist('@timestamp[. < sql:variable("@EndDate")]') = 1 + AND e.x.exist('@timestamp[. >= sql:variable("@StartDate") and .< sql:variable("@EndDate")]') = 1 OPTION(RECOMPILE); IF @Debug = 1 BEGIN SET STATISTICS XML OFF; END; @@ -931,8 +929,7 @@ BEGIN ) AS xml CROSS APPLY xml.deadlock_xml.nodes('/event') AS e(x) WHERE 1 = 1 - AND e.x.exist('@timestamp[. >= sql:variable("@StartDate")]') = 1 - AND e.x.exist('@timestamp[. < sql:variable("@EndDate")]') = 1 + AND e.x.exist('@timestamp[. >= sql:variable("@StartDate") and .< sql:variable("@EndDate")]') = 1 OPTION(RECOMPILE); INSERT From 1ae5fc7bc3a50e4b29ee264394fef97c7ebcd9d0 Mon Sep 17 00:00:00 2001 From: Erik Darling <2136037+erikdarlingdata@users.noreply.github.com> Date: Sun, 16 Jul 2023 10:03:21 -0400 Subject: [PATCH 13/30] Adds check for session existence I should probably add a to-do that also checks to make sure that the event has an xml deadlock report of some variety assigned to it, but Nicht heute, Satan Closes #3306 --- sp_BlitzLock.sql | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/sp_BlitzLock.sql b/sp_BlitzLock.sql index 1a85d612..12a023a8 100644 --- a/sp_BlitzLock.sql +++ b/sp_BlitzLock.sql @@ -227,8 +227,8 @@ BEGIN /*Normally I'd hate this, but we RECOMPILE everything*/ SELECT @StartDate = - CASE - WHEN @StartDate IS NULL + CASE + WHEN @StartDate IS NULL THEN DATEADD ( @@ -274,6 +274,42 @@ BEGIN ELSE @EndDate END; + IF @Azure = 0 + BEGIN + IF NOT EXISTS + ( + SELECT + 1/0 + FROM sys.server_event_sessions AS ses + JOIN sys.dm_xe_sessions AS dxs + ON dxs.name = ses.name + WHERE ses.name = @EventSessionName + AND dxs.create_time IS NOT NULL + ) + BEGIN + RAISERROR('A session with the name %s does not exist or is not currently active.', 11, 1, @session_name) WITH NOWAIT; + RETURN; + END; + END; + + IF @Azure = 1 + BEGIN + IF NOT EXISTS + ( + SELECT + 1/0 + FROM sys.database_event_sessions AS ses + JOIN sys.dm_xe_database_sessions AS dxs + ON dxs.name = ses.name + WHERE ses.name = @EventSessionName + AND dxs.create_time IS NOT NULL + ) + BEGIN + RAISERROR('A session with the name %s does not exist or is not currently active.', 11, 1, @session_name) WITH NOWAIT; + RETURN; + END; + END; + IF @OutputDatabaseName IS NOT NULL BEGIN /*IF databaseName is set, do some sanity checks and put [] around def.*/ SET @d = CONVERT(varchar(40), GETDATE(), 109); From b4a8c1082e4b8226c38beaa13777998669e9222d Mon Sep 17 00:00:00 2001 From: Erik Darling <2136037+erikdarlingdata@users.noreply.github.com> Date: Sun, 16 Jul 2023 10:11:08 -0400 Subject: [PATCH 14/30] Update sp_BlitzLock.sql Fix raiserror --- sp_BlitzLock.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sp_BlitzLock.sql b/sp_BlitzLock.sql index 12a023a8..7a44d676 100644 --- a/sp_BlitzLock.sql +++ b/sp_BlitzLock.sql @@ -287,7 +287,7 @@ BEGIN AND dxs.create_time IS NOT NULL ) BEGIN - RAISERROR('A session with the name %s does not exist or is not currently active.', 11, 1, @session_name) WITH NOWAIT; + RAISERROR('A session with the name %s does not exist or is not currently active.', 11, 1, @EventSessionName) WITH NOWAIT; RETURN; END; END; @@ -305,7 +305,7 @@ BEGIN AND dxs.create_time IS NOT NULL ) BEGIN - RAISERROR('A session with the name %s does not exist or is not currently active.', 11, 1, @session_name) WITH NOWAIT; + RAISERROR('A session with the name %s does not exist or is not currently active.', 11, 1, @EventSessionName) WITH NOWAIT; RETURN; END; END; From 238c70f893032b3dd477be256523682ca2c6e9d5 Mon Sep 17 00:00:00 2001 From: Erik Darling <2136037+erikdarlingdata@users.noreply.github.com> Date: Sun, 16 Jul 2023 11:09:24 -0400 Subject: [PATCH 15/30] Update sp_BlitzLock.sql Closes #3307 In this commit: * Adjust start and end dates to make them UTC-friendly for initial XML searches * Revert them back to the originals for subsequent searches, since the time gets converted to local after extraction from the event XML * Add parameter and variable output to the debug section for improved troubleshooting (boy did I need that!) * Minor formatting/whitespace changes --- sp_BlitzLock.sql | 291 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 208 insertions(+), 83 deletions(-) diff --git a/sp_BlitzLock.sql b/sp_BlitzLock.sql index 7a44d676..36f92b65 100644 --- a/sp_BlitzLock.sql +++ b/sp_BlitzLock.sql @@ -179,7 +179,9 @@ BEGIN @TargetSessionId int = 0, @FileName nvarchar(4000) = N'', @inputbuf_bom nvarchar(1) = CONVERT(nvarchar(1), 0x0a00, 0), - @deadlock_result nvarchar(MAX) = N''; + @deadlock_result nvarchar(MAX) = N'', + @StartDateOriginal datetime = @StartDate, + @EndDateOriginal datetime = @EndDate; /*Temporary objects used in the procedure*/ DECLARE @@ -220,11 +222,12 @@ BEGIN object_name nvarchar(1000), finding_group nvarchar(100), finding nvarchar(4000), - sort_order bigint + sort_order bigint ); /*Set these to some sane defaults if NULLs are passed in*/ /*Normally I'd hate this, but we RECOMPILE everything*/ + SELECT @StartDate = CASE @@ -250,7 +253,18 @@ BEGIN ) ) ) - ELSE @StartDate + ELSE + DATEADD + ( + MINUTE, + DATEDIFF + ( + MINUTE, + SYSDATETIME(), + GETUTCDATE() + ), + @StartDate + ) END, @EndDate = CASE @@ -271,7 +285,18 @@ BEGIN SYSDATETIME() ) ) - ELSE @EndDate + ELSE + DATEADD + ( + MINUTE, + DATEDIFF + ( + MINUTE, + SYSDATETIME(), + GETUTCDATE() + ), + @EndDate + ) END; IF @Azure = 0 @@ -291,7 +316,7 @@ BEGIN RETURN; END; END; - + IF @Azure = 1 BEGIN IF NOT EXISTS @@ -1092,7 +1117,12 @@ BEGIN DATEADD ( MINUTE, - DATEDIFF(MINUTE, GETUTCDATE(), SYSDATETIME()), + DATEDIFF + ( + MINUTE, + GETUTCDATE(), + SYSDATETIME() + ), dd.event_date ), dd.victim_id, @@ -1223,7 +1253,7 @@ BEGIN waiter_mode = w.l.value('@mode', 'nvarchar(256)'), owner_id = o.l.value('@id', 'nvarchar(256)'), owner_mode = o.l.value('@mode', 'nvarchar(256)'), - lock_type = CAST(N'OBJECT' AS NVARCHAR(100)) + lock_type = CAST(N'OBJECT' AS nvarchar(100)) INTO #deadlock_owner_waiter FROM ( @@ -1781,6 +1811,14 @@ BEGIN /*Begin checks based on parsed values*/ + /* + First, revert these back since we already converted the event data to local time, + and searches will break if we use the times converted over to UTC for the event data + */ + SELECT + @StartDate = @StartDateOriginal, + @EndDate = @EndDateOriginal; + /*Check 1 is deadlocks by database*/ SET @d = CONVERT(varchar(40), GETDATE(), 109); RAISERROR('Check 1 database deadlocks %s', 0, 1, @d) WITH NOWAIT; @@ -1793,7 +1831,7 @@ BEGIN object_name, finding_group, finding, - sort_order + sort_order ) SELECT check_id = 1, @@ -1808,9 +1846,9 @@ BEGIN COUNT_BIG(DISTINCT dp.event_date) ) + N' deadlocks.', - sort_order = + sort_order = ROW_NUMBER() - OVER (ORDER BY COUNT_BIG(DISTINCT dp.event_date) DESC) + OVER (ORDER BY COUNT_BIG(DISTINCT dp.event_date) DESC) FROM #deadlock_process AS dp WHERE 1 = 1 AND (dp.database_name = @DatabaseName OR @DatabaseName IS NULL) @@ -1836,7 +1874,7 @@ BEGIN object_name, finding_group, finding, - sort_order + sort_order ) SELECT check_id = 2, @@ -1852,9 +1890,9 @@ BEGIN COUNT_BIG(DISTINCT dow.event_date) ) + N' deadlock(s) between read queries and modification queries.', - sort_order = + sort_order = ROW_NUMBER() - OVER (ORDER BY COUNT_BIG(DISTINCT dow.event_date) DESC) + OVER (ORDER BY COUNT_BIG(DISTINCT dow.event_date) DESC) FROM #deadlock_owner_waiter AS dow WHERE 1 = 1 AND dow.lock_mode IN @@ -1894,7 +1932,7 @@ BEGIN object_name, finding_group, finding, - sort_order + sort_order ) SELECT check_id = 3, @@ -1914,9 +1952,9 @@ BEGIN COUNT_BIG(DISTINCT dow.event_date) ) + N' deadlock(s).', - sort_order = + sort_order = ROW_NUMBER() - OVER (ORDER BY COUNT_BIG(DISTINCT dow.event_date) DESC) + OVER (ORDER BY COUNT_BIG(DISTINCT dow.event_date) DESC) FROM #deadlock_owner_waiter AS dow WHERE 1 = 1 AND (dow.database_id = @DatabaseId OR @DatabaseName IS NULL) @@ -1942,7 +1980,7 @@ BEGIN object_name, finding_group, finding, - sort_order + sort_order ) SELECT check_id = 3, @@ -1957,9 +1995,9 @@ BEGIN COUNT_BIG(DISTINCT dow.event_date) ) + N' deadlock(s).', - sort_order = + sort_order = ROW_NUMBER() - OVER (ORDER BY COUNT_BIG(DISTINCT dow.event_date) DESC) + OVER (ORDER BY COUNT_BIG(DISTINCT dow.event_date) DESC) FROM #deadlock_owner_waiter AS dow WHERE 1 = 1 AND (dow.database_id = @DatabaseId OR @DatabaseName IS NULL) @@ -1991,7 +2029,7 @@ BEGIN object_name, finding_group, finding, - sort_order + sort_order ) SELECT check_id = 3, @@ -2006,9 +2044,9 @@ BEGIN COUNT_BIG(DISTINCT dow.event_date) ) + N' deadlock(s).', - sort_order = + sort_order = ROW_NUMBER() - OVER (ORDER BY COUNT_BIG(DISTINCT dow.event_date) DESC) + OVER (ORDER BY COUNT_BIG(DISTINCT dow.event_date) DESC) FROM #deadlock_owner_waiter AS dow WHERE 1 = 1 AND (dow.database_id = @DatabaseId OR @DatabaseName IS NULL) @@ -2039,7 +2077,7 @@ BEGIN object_name, finding_group, finding, - sort_order + sort_order ) SELECT check_id = 4, @@ -2055,9 +2093,9 @@ BEGIN COUNT_BIG(DISTINCT dp.event_date) ) + N' instances of Serializable deadlocks.', - sort_order = + sort_order = ROW_NUMBER() - OVER (ORDER BY COUNT_BIG(DISTINCT dp.event_date) DESC) + OVER (ORDER BY COUNT_BIG(DISTINCT dp.event_date) DESC) FROM #deadlock_process AS dp WHERE dp.isolation_level LIKE N'serializable%' AND (dp.database_name = @DatabaseName OR @DatabaseName IS NULL) @@ -2083,7 +2121,7 @@ BEGIN object_name, finding_group, finding, - sort_order + sort_order ) SELECT check_id = 5, @@ -2098,9 +2136,9 @@ BEGIN COUNT_BIG(DISTINCT dp.event_date) ) + N' instances of Repeatable Read deadlocks.', - sort_order = + sort_order = ROW_NUMBER() - OVER (ORDER BY COUNT_BIG(DISTINCT dp.event_date) DESC) + OVER (ORDER BY COUNT_BIG(DISTINCT dp.event_date) DESC) FROM #deadlock_process AS dp WHERE dp.isolation_level LIKE N'repeatable%' AND (dp.database_name = @DatabaseName OR @DatabaseName IS NULL) @@ -2126,7 +2164,7 @@ BEGIN object_name, finding_group, finding, - sort_order + sort_order ) SELECT check_id = 6, @@ -2160,9 +2198,9 @@ BEGIN N'UNKNOWN' ) + N'.', - sort_order = + sort_order = ROW_NUMBER() - OVER (ORDER BY COUNT_BIG(DISTINCT dp.event_date) DESC) + OVER (ORDER BY COUNT_BIG(DISTINCT dp.event_date) DESC) FROM #deadlock_process AS dp WHERE 1 = 1 AND (dp.database_name = @DatabaseName OR @DatabaseName IS NULL) @@ -2244,7 +2282,7 @@ BEGIN object_name, finding_group, finding, - sort_order + sort_order ) SELECT check_id = 7, @@ -2272,9 +2310,9 @@ BEGIN 1, N'' ) + N' locks.', - sort_order = + sort_order = ROW_NUMBER() - OVER (ORDER BY CONVERT(bigint, lt.lock_count) DESC) + OVER (ORDER BY CONVERT(bigint, lt.lock_count) DESC) FROM lock_types AS lt OPTION(RECOMPILE); @@ -2431,7 +2469,7 @@ BEGIN object_name, finding_group, finding, - sort_order + sort_order ) SELECT check_id = 9, @@ -2451,9 +2489,9 @@ BEGIN COUNT_BIG(DISTINCT ds.id) ) + N' deadlocks.', - sort_order = + sort_order = ROW_NUMBER() - OVER (ORDER BY COUNT_BIG(DISTINCT ds.id) DESC) + OVER (ORDER BY COUNT_BIG(DISTINCT ds.id) DESC) FROM #deadlock_stack AS ds JOIN #deadlock_process AS dp ON dp.id = ds.id @@ -2552,19 +2590,19 @@ BEGIN ) ), wait_time_hms = - /*the more wait time you rack up the less accurate this gets, + /*the more wait time you rack up the less accurate this gets, it's either that or erroring out*/ - CASE - WHEN + CASE + WHEN SUM ( CONVERT ( - bigint, + bigint, dp.wait_time ) )/1000 > 2147483647 - THEN + THEN CONVERT ( nvarchar(30), @@ -2577,7 +2615,7 @@ BEGIN ( CONVERT ( - bigint, + bigint, dp.wait_time ) ) @@ -2588,16 +2626,16 @@ BEGIN ), 14 ) - WHEN + WHEN SUM ( CONVERT ( - bigint, + bigint, dp.wait_time ) ) BETWEEN 2147483648 AND 2147483647000 - THEN + THEN CONVERT ( nvarchar(30), @@ -2610,7 +2648,7 @@ BEGIN ( CONVERT ( - bigint, + bigint, dp.wait_time ) ) @@ -2643,8 +2681,8 @@ BEGIN 14 ) END, - total_waits = - SUM(CONVERT(bigint, dp.wait_time)) + total_waits = + SUM(CONVERT(bigint, dp.wait_time)) FROM #deadlock_owner_waiter AS dow JOIN #deadlock_process AS dp ON (dp.id = dow.owner_id @@ -2670,7 +2708,7 @@ BEGIN object_name, finding_group, finding, - sort_order + sort_order ) SELECT check_id = 11, @@ -2692,9 +2730,9 @@ BEGIN 14 ) + N' [dd hh:mm:ss:ms] of deadlock wait time.', - sort_order = + sort_order = ROW_NUMBER() - OVER (ORDER BY cs.total_waits DESC) + OVER (ORDER BY cs.total_waits DESC) FROM chopsuey AS cs WHERE cs.object_name IS NOT NULL OPTION(RECOMPILE); @@ -2743,7 +2781,7 @@ BEGIN object_name, finding_group, finding, - sort_order + sort_order ) SELECT check_id = 12, @@ -2766,19 +2804,19 @@ BEGIN ) ) + N' ' + - /*the more wait time you rack up the less accurate this gets, + /*the more wait time you rack up the less accurate this gets, it's either that or erroring out*/ - CASE - WHEN + CASE + WHEN SUM ( CONVERT ( - bigint, + bigint, wt.total_wait_time_ms ) )/1000 > 2147483647 - THEN + THEN CONVERT ( nvarchar(30), @@ -2791,7 +2829,7 @@ BEGIN ( CONVERT ( - bigint, + bigint, wt.total_wait_time_ms ) ) @@ -2802,16 +2840,16 @@ BEGIN ), 14 ) - WHEN + WHEN SUM ( CONVERT ( - bigint, + bigint, wt.total_wait_time_ms ) ) BETWEEN 2147483648 AND 2147483647000 - THEN + THEN CONVERT ( nvarchar(30), @@ -2824,7 +2862,7 @@ BEGIN ( CONVERT ( - bigint, + bigint, wt.total_wait_time_ms ) ) @@ -2857,9 +2895,9 @@ BEGIN 14 ) END + N' [dd hh:mm:ss:ms] of deadlock wait time.', - sort_order = + sort_order = ROW_NUMBER() - OVER (ORDER BY SUM(CONVERT(bigint, wt.total_wait_time_ms)) DESC) + OVER (ORDER BY SUM(CONVERT(bigint, wt.total_wait_time_ms)) DESC) FROM wait_time AS wt GROUP BY wt.database_name @@ -2879,7 +2917,7 @@ BEGIN object_name, finding_group, finding, - sort_order + sort_order ) SELECT check_id = 13, @@ -2895,9 +2933,9 @@ BEGIN N'There have been ' + RTRIM(COUNT_BIG(DISTINCT aj.event_date)) + N' deadlocks from this Agent Job and Step.', - sort_order = + sort_order = ROW_NUMBER() - OVER (ORDER BY COUNT_BIG(DISTINCT aj.event_date) DESC) + OVER (ORDER BY COUNT_BIG(DISTINCT aj.event_date) DESC) FROM #agent_job AS aj GROUP BY DB_NAME(aj.database_id), @@ -3648,18 +3686,18 @@ BEGIN BEGIN SET @d = CONVERT(varchar(40), GETDATE(), 109); RAISERROR('Results to client %s', 0, 1, @d) WITH NOWAIT; - + IF @Debug = 1 BEGIN SET STATISTICS XML ON; END; - + EXEC sys.sp_executesql @deadlock_result; - + IF @Debug = 1 BEGIN SET STATISTICS XML OFF; PRINT @deadlock_result; END; - + RAISERROR('Finished at %s', 0, 1, @d) WITH NOWAIT; SET @d = CONVERT(varchar(40), GETDATE(), 109); @@ -3747,7 +3785,7 @@ BEGIN executions_per_second = ISNULL ( - execution_count / + deqs.execution_count / NULLIF ( DATEDIFF @@ -3781,7 +3819,7 @@ BEGIN min_spills_mb = deqs.min_spills * 8. / 1024., max_spills_mb = - deqs.max_spills * 8. / 1024., + deqs.max_spills * 8. / 1024., deqs.min_reserved_threads, deqs.max_reserved_threads, deqs.min_used_threads, @@ -3808,10 +3846,10 @@ BEGIN OPTION(RECOMPILE); RAISERROR('Finished at %s', 0, 1, @d) WITH NOWAIT; - + SET @d = CONVERT(varchar(40), GETDATE(), 109); RAISERROR('Returning findings %s', 0, 1, @d) WITH NOWAIT; - + SELECT df.check_id, df.database_name, @@ -3820,10 +3858,10 @@ BEGIN df.finding FROM #deadlock_findings AS df ORDER BY - df.check_id, - df.sort_order + df.check_id, + df.sort_order OPTION(RECOMPILE); - + SET @d = CONVERT(varchar(40), GETDATE(), 109); RAISERROR('Finished at %s', 0, 1, @d) WITH NOWAIT; END; /*done with output to client app.*/ @@ -3832,15 +3870,15 @@ BEGIN IF @Debug = 1 BEGIN SELECT - table_name = N'#dd', + table_name = N'#deadlock_data', * - FROM #dd AS d + FROM #deadlock_data AS dd OPTION(RECOMPILE); SELECT - table_name = N'#deadlock_data', + table_name = N'#dd', * - FROM #deadlock_data AS dd + FROM #dd AS d OPTION(RECOMPILE); SELECT @@ -3897,6 +3935,93 @@ BEGIN FROM @sysAssObjId AS s OPTION(RECOMPILE); + SELECT + procedure_parameters = + 'procedure_parameters', + DatabaseName = + @DatabaseName, + StartDate = + @StartDate, + EndDate = + @EndDate, + ObjectName = + @ObjectName, + StoredProcName = + @StoredProcName, + AppName = + @AppName, + HostName = + @HostName, + LoginName = + @LoginName, + EventSessionName = + @EventSessionName, + TargetSessionType = + @TargetSessionType, + VictimsOnly = + @VictimsOnly, + Debug = + @Debug, + Help = + @Help, + Version = + @Version, + VersionDate = + @VersionDate, + VersionCheckMode = + @VersionCheckMode, + OutputDatabaseName = + @OutputDatabaseName, + OutputSchemaName = + @OutputSchemaName, + OutputTableName = + @OutputTableName, + ExportToExcel = + @ExportToExcel; + + SELECT + declared_variables = + 'declared_variables', + DatabaseId = + @DatabaseId, + ProductVersion = + @ProductVersion, + ProductVersionMajor = + @ProductVersionMajor, + ProductVersionMinor = + @ProductVersionMinor, + ObjectFullName = + @ObjectFullName, + Azure = + @Azure, + RDS = + @RDS, + d = + @d, + StringToExecute = + @StringToExecute, + StringToExecuteParams = + @StringToExecuteParams, + r = + @r, + OutputTableFindings = + @OutputTableFindings, + DeadlockCount = + @DeadlockCount, + ServerName = + @ServerName, + OutputDatabaseCheck = + @OutputDatabaseCheck, + SessionId = + @SessionId, + TargetSessionId = + @TargetSessionId, + FileName = + @FileName, + inputbuf_bom = + @inputbuf_bom, + deadlock_result = + @deadlock_result; END; /*End debug*/ END; /*Final End*/ From daac34090cca6f3172610275d1a601a12e37ea67 Mon Sep 17 00:00:00 2001 From: Erik Darling <2136037+erikdarlingdata@users.noreply.github.com> Date: Mon, 24 Jul 2023 12:55:22 -0400 Subject: [PATCH 16/30] Update sp_BlitzLock.sql Closes #3311 --- sp_BlitzLock.sql | 234 +++++++++++++++++++++++++++-------------------- 1 file changed, 136 insertions(+), 98 deletions(-) diff --git a/sp_BlitzLock.sql b/sp_BlitzLock.sql index 36f92b65..54d55452 100644 --- a/sp_BlitzLock.sql +++ b/sp_BlitzLock.sql @@ -181,7 +181,9 @@ BEGIN @inputbuf_bom nvarchar(1) = CONVERT(nvarchar(1), 0x0a00, 0), @deadlock_result nvarchar(MAX) = N'', @StartDateOriginal datetime = @StartDate, - @EndDateOriginal datetime = @EndDate; + @EndDateOriginal datetime = @EndDate, + @StartDateUTC datetime, + @EndDateUTC datetime; /*Temporary objects used in the procedure*/ DECLARE @@ -242,15 +244,11 @@ BEGIN SYSDATETIME(), GETUTCDATE() ), - ISNULL + DATEADD ( - @StartDate, - DATEADD - ( - DAY, - -7, - SYSDATETIME() - ) + DAY, + -7, + SYSDATETIME() ) ) ELSE @@ -279,11 +277,7 @@ BEGIN SYSDATETIME(), GETUTCDATE() ), - ISNULL - ( - @EndDate, - SYSDATETIME() - ) + SYSDATETIME() ) ELSE DATEADD @@ -299,6 +293,10 @@ BEGIN ) END; + SELECT + @StartDateUTC = @StartDate, + @EndDateUTC = @EndDate; + IF @Azure = 0 BEGIN IF NOT EXISTS @@ -1812,9 +1810,9 @@ BEGIN /*Begin checks based on parsed values*/ /* - First, revert these back since we already converted the event data to local time, - and searches will break if we use the times converted over to UTC for the event data - */ + First, revert these back since we already converted the event data to local time, + and searches will break if we use the times converted over to UTC for the event data + */ SELECT @StartDate = @StartDateOriginal, @EndDate = @EndDateOriginal; @@ -3702,26 +3700,104 @@ BEGIN SET @d = CONVERT(varchar(40), GETDATE(), 109); RAISERROR('Getting available execution plans for deadlocks %s', 0, 1, @d) WITH NOWAIT; - + SELECT DISTINCT - ds.proc_name, + available_plans = + 'available_plans', + ds.proc_name, sql_handle = CONVERT(varbinary(64), ds.sql_handle, 1), dow.database_name, - dow.object_name, + dow.database_id, + dow.object_name, query_xml = - CONVERT(nvarchar(MAX), dr.query_xml) + TRY_CAST(dr.query_xml AS nvarchar(MAX)) INTO #available_plans FROM #deadlock_stack AS ds JOIN #deadlock_owner_waiter AS dow ON dow.owner_id = ds.id AND dow.event_date = ds.event_date JOIN #deadlock_results AS dr - ON dr.id = ds.id - AND dr.event_date = ds.event_date + ON dr.id = ds.id + AND dr.event_date = ds.event_date OPTION(RECOMPILE); SELECT + deqs.sql_handle, + deqs.plan_handle, + deqs.statement_start_offset, + deqs.statement_end_offset, + deqs.creation_time, + deqs.last_execution_time, + deqs.execution_count, + total_worker_time_ms = + deqs.total_worker_time / 1000., + avg_worker_time_ms = + CONVERT(decimal(38, 6), deqs.total_worker_time / 1000. / deqs.execution_count), + total_elapsed_time_ms = + deqs.total_elapsed_time / 1000., + avg_elapsed_time = + CONVERT(decimal(38, 6), deqs.total_elapsed_time / 1000. / deqs.execution_count), + executions_per_second = + ISNULL + ( + deqs.execution_count / + NULLIF + ( + DATEDIFF + ( + SECOND, + deqs.creation_time, + deqs.last_execution_time + ), + 0 + ), + 0 + ), + total_physical_reads_mb = + deqs.total_physical_reads * 8. / 1024., + total_logical_writes_mb = + deqs.total_logical_writes * 8. / 1024., + total_logical_reads_mb = + deqs.total_logical_reads * 8. / 1024., + min_grant_mb = + deqs.min_grant_kb * 8. / 1024., + max_grant_mb = + deqs.max_grant_kb * 8. / 1024., + min_used_grant_mb = + deqs.min_used_grant_kb * 8. / 1024., + max_used_grant_mb = + deqs.max_used_grant_kb * 8. / 1024., + min_spills_mb = + deqs.min_spills * 8. / 1024., + max_spills_mb = + deqs.max_spills * 8. / 1024., + deqs.min_reserved_threads, + deqs.max_reserved_threads, + deqs.min_used_threads, + deqs.max_used_threads, + deqs.total_rows + INTO #dm_exec_query_stats + FROM sys.dm_exec_query_stats AS deqs + WHERE EXISTS + ( + SELECT + 1/0 + FROM #available_plans AS ap + WHERE ap.sql_handle = deqs.sql_handle + ) + AND deqs.query_hash IS NOT NULL; + + CREATE CLUSTERED INDEX + deqs + ON #dm_exec_query_stats + ( + sql_handle, + plan_handle + ); + + SELECT + ap.available_plans, ap.database_name, query_text = TRY_CAST(ap.query_xml AS xml), @@ -3736,8 +3812,6 @@ BEGIN ap.avg_elapsed_time, ap.total_logical_reads_mb, ap.total_physical_reads_mb, - ap.min_physical_reads_mb, - ap.max_physical_reads_mb, ap.total_logical_writes_mb, ap.min_grant_mb, ap.max_grant_mb, @@ -3755,79 +3829,42 @@ BEGIN ap.statement_end_offset FROM ( + SELECT - *, - n = - ROW_NUMBER() OVER - ( - PARTITION BY - ap.sql_handle - ORDER BY - ap.sql_handle - ) + ap.*, + c.statement_start_offset, + c.statement_end_offset, + c.creation_time, + c.last_execution_time, + c.execution_count, + c.total_worker_time_ms, + c.avg_worker_time_ms, + c.total_elapsed_time_ms, + c.avg_elapsed_time, + c.executions_per_second, + c.total_physical_reads_mb, + c.total_logical_writes_mb, + c.total_logical_reads_mb, + c.min_grant_mb, + c.max_grant_mb, + c.min_used_grant_mb, + c.max_used_grant_mb, + c.min_spills_mb, + c.max_spills_mb, + c.min_reserved_threads, + c.max_reserved_threads, + c.min_used_threads, + c.max_used_threads, + c.total_rows, + c.query_plan FROM #available_plans AS ap OUTER APPLY ( - SELECT TOP (1) - deqs.statement_start_offset, - deqs.statement_end_offset, - deqs.creation_time, - deqs.last_execution_time, - deqs.execution_count, - total_worker_time_ms = - deqs.total_worker_time / 1000., - avg_worker_time_ms = - CONVERT(decimal(38, 6), deqs.total_worker_time / 1000. / deqs.execution_count), - total_elapsed_time_ms = - deqs.total_elapsed_time / 1000., - avg_elapsed_time = - CONVERT(decimal(38, 6), deqs.total_elapsed_time / 1000. / deqs.execution_count), - executions_per_second = - ISNULL - ( - deqs.execution_count / - NULLIF - ( - DATEDIFF - ( - SECOND, - deqs.creation_time, - deqs.last_execution_time - ), - 0 - ), - 0 - ), - total_physical_reads_mb = - deqs.total_physical_reads * 8. / 1024., - total_logical_writes_mb = - deqs.total_logical_writes * 8. / 1024., - total_logical_reads_mb = - deqs.total_logical_reads * 8. / 1024., - min_physical_reads_mb = - deqs.min_physical_reads * 8. / 1024., - max_physical_reads_mb = - deqs.max_physical_reads * 8. / 1024., - min_grant_mb = - deqs.min_grant_kb * 8. / 1024., - max_grant_mb = - deqs.max_grant_kb * 8. / 1024., - min_used_grant_mb = - deqs.min_used_grant_kb * 8. / 1024., - max_used_grant_mb = - deqs.max_used_grant_kb * 8. / 1024., - min_spills_mb = - deqs.min_spills * 8. / 1024., - max_spills_mb = - deqs.max_spills * 8. / 1024., - deqs.min_reserved_threads, - deqs.max_reserved_threads, - deqs.min_used_threads, - deqs.max_used_threads, - deqs.total_rows, + SELECT + deqs.*, query_plan = TRY_CAST(deps.query_plan AS xml) - FROM sys.dm_exec_query_stats AS deqs + FROM #dm_exec_query_stats deqs OUTER APPLY sys.dm_exec_text_query_plan ( deqs.plan_handle, @@ -3835,15 +3872,13 @@ BEGIN deqs.statement_end_offset ) AS deps WHERE deqs.sql_handle = ap.sql_handle - ORDER BY - deqs.last_execution_time DESC + AND deps.dbid = ap.database_id ) AS c ) AS ap WHERE ap.query_plan IS NOT NULL - AND ap.n = 1 ORDER BY - ap.last_execution_time DESC - OPTION(RECOMPILE); + ap.avg_worker_time_ms DESC + OPTION(RECOMPILE, LOOP JOIN, HASH JOIN); RAISERROR('Finished at %s', 0, 1, @d) WITH NOWAIT; @@ -3984,6 +4019,10 @@ BEGIN 'declared_variables', DatabaseId = @DatabaseId, + StartDateUTC = + @StartDateUTC, + EndDateUTC = + @EndDateUTC, ProductVersion = @ProductVersion, ProductVersionMajor = @@ -4024,5 +4063,4 @@ BEGIN @deadlock_result; END; /*End debug*/ END; /*Final End*/ - GO From 73600c9c3194922bd3ed92d88cc47b9899d90a46 Mon Sep 17 00:00:00 2001 From: Erik Darling <2136037+erikdarlingdata@users.noreply.github.com> Date: Wed, 26 Jul 2023 11:03:41 -0400 Subject: [PATCH 17/30] Update sp_BlitzLock.sql Pull spills columns since they break compat pre-2016 and I'm still too lazy to make this dynamic. --- sp_BlitzLock.sql | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/sp_BlitzLock.sql b/sp_BlitzLock.sql index 54d55452..d9d3760d 100644 --- a/sp_BlitzLock.sql +++ b/sp_BlitzLock.sql @@ -3767,11 +3767,7 @@ BEGIN min_used_grant_mb = deqs.min_used_grant_kb * 8. / 1024., max_used_grant_mb = - deqs.max_used_grant_kb * 8. / 1024., - min_spills_mb = - deqs.min_spills * 8. / 1024., - max_spills_mb = - deqs.max_spills * 8. / 1024., + deqs.max_used_grant_kb * 8. / 1024., deqs.min_reserved_threads, deqs.max_reserved_threads, deqs.min_used_threads, @@ -3817,8 +3813,6 @@ BEGIN ap.max_grant_mb, ap.min_used_grant_mb, ap.max_used_grant_mb, - ap.min_spills_mb, - ap.max_spills_mb, ap.min_reserved_threads, ap.max_reserved_threads, ap.min_used_threads, @@ -3849,8 +3843,6 @@ BEGIN c.max_grant_mb, c.min_used_grant_mb, c.max_used_grant_mb, - c.min_spills_mb, - c.max_spills_mb, c.min_reserved_threads, c.max_reserved_threads, c.min_used_threads, From 9d7f7ea6c787efd44c4adfc5884d37fd21cf7688 Mon Sep 17 00:00:00 2001 From: Walden Leverich Date: Wed, 26 Jul 2023 20:10:38 -0500 Subject: [PATCH 18/30] Omit Resource DB from duplicate query plan checks Addresses #3314 --- sp_BlitzCache.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/sp_BlitzCache.sql b/sp_BlitzCache.sql index 11600c64..fb41ff60 100644 --- a/sp_BlitzCache.sql +++ b/sp_BlitzCache.sql @@ -1620,6 +1620,7 @@ WITH total_plans AS ON qs.sql_handle = ps.sql_handle CROSS APPLY sys.dm_exec_plan_attributes(qs.plan_handle) pa WHERE pa.attribute = N'dbid' + AND pa.value <> 32767 /*Omit Resource database-based queries, we're not going to "fix" them no matter what. Addresses #3314*/ AND qs.query_plan_hash <> 0x0000000000000000 GROUP BY /* qs.query_plan_hash, BGO 20210524 commenting this out to fix #2909 */ From 7320f75ecdc222ecfaadd660b9b17fb2a8a822f7 Mon Sep 17 00:00:00 2001 From: David Schanzer Date: Thu, 27 Jul 2023 17:12:23 +1000 Subject: [PATCH 19/30] Correct bracketing for Replication In Use check --- sp_Blitz.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sp_Blitz.sql b/sp_Blitz.sql index 43657817..0ef90c31 100644 --- a/sp_Blitz.sql +++ b/sp_Blitz.sql @@ -6579,10 +6579,10 @@ IF @ProductVersionMajor >= 10 DatabaseName FROM #SkipChecks WHERE CheckID IS NULL OR CheckID = 19) - AND is_published = 1 + AND (is_published = 1 OR is_subscribed = 1 OR is_merge_published = 1 - OR is_distributor = 1; + OR is_distributor = 1); /* Method B: check subscribers for MSreplication_objects tables */ EXEC dbo.sp_MSforeachdb 'USE [?]; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; From af157326f55548eea614c692cb3830409ae49b00 Mon Sep 17 00:00:00 2001 From: Erik Darling <2136037+erikdarlingdata@users.noreply.github.com> Date: Thu, 27 Jul 2023 11:15:44 -0400 Subject: [PATCH 20/30] Update sp_BlitzLock.sql Add the available plans and exec query stats temp tables to debug output --- sp_BlitzLock.sql | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/sp_BlitzLock.sql b/sp_BlitzLock.sql index d9d3760d..f1278750 100644 --- a/sp_BlitzLock.sql +++ b/sp_BlitzLock.sql @@ -293,9 +293,9 @@ BEGIN ) END; - SELECT - @StartDateUTC = @StartDate, - @EndDateUTC = @EndDate; + SELECT + @StartDateUTC = @StartDate, + @EndDateUTC = @EndDate; IF @Azure = 0 BEGIN @@ -3700,16 +3700,16 @@ BEGIN SET @d = CONVERT(varchar(40), GETDATE(), 109); RAISERROR('Getting available execution plans for deadlocks %s', 0, 1, @d) WITH NOWAIT; - + SELECT DISTINCT - available_plans = - 'available_plans', - ds.proc_name, + available_plans = + 'available_plans', + ds.proc_name, sql_handle = CONVERT(varbinary(64), ds.sql_handle, 1), dow.database_name, - dow.database_id, - dow.object_name, + dow.database_id, + dow.object_name, query_xml = TRY_CAST(dr.query_xml AS nvarchar(MAX)) INTO #available_plans @@ -3823,7 +3823,7 @@ BEGIN ap.statement_end_offset FROM ( - + SELECT ap.*, c.statement_start_offset, @@ -3962,6 +3962,18 @@ BEGIN FROM @sysAssObjId AS s OPTION(RECOMPILE); + SELECT + table_name = N'#available_plans', + * + FROM #available_plans AS ap + OPTION(RECOMPILE); + + SELECT + table_name = N'ava#dm_exec_query_statsilable_plans', + * + FROM #dm_exec_query_stats + OPTION(RECOMPILE); + SELECT procedure_parameters = 'procedure_parameters', From 717b5da58fa6273cf31fbb17bcf77d7791a2eceb Mon Sep 17 00:00:00 2001 From: Erik Darling <2136037+erikdarlingdata@users.noreply.github.com> Date: Thu, 27 Jul 2023 13:13:30 -0400 Subject: [PATCH 21/30] Update sp_BlitzLock.sql Well that was unfortunate. --- sp_BlitzLock.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp_BlitzLock.sql b/sp_BlitzLock.sql index f1278750..b609731f 100644 --- a/sp_BlitzLock.sql +++ b/sp_BlitzLock.sql @@ -3969,7 +3969,7 @@ BEGIN OPTION(RECOMPILE); SELECT - table_name = N'ava#dm_exec_query_statsilable_plans', + table_name = N'#dm_exec_query_stats', * FROM #dm_exec_query_stats OPTION(RECOMPILE); From b70b326e35d384a0b5b576612303fa111c3020cb Mon Sep 17 00:00:00 2001 From: PasswordIsMyPassword Date: Sat, 29 Jul 2023 17:10:00 -0500 Subject: [PATCH 22/30] #3314 sp_BlitzCache high percentage of duplicate plans Fixes #3314 --- sp_BlitzCache.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp_BlitzCache.sql b/sp_BlitzCache.sql index 11600c64..aa4d6450 100644 --- a/sp_BlitzCache.sql +++ b/sp_BlitzCache.sql @@ -1617,7 +1617,7 @@ WITH total_plans AS COUNT_BIG(qs.query_plan_hash) AS duplicate_plan_hashes FROM sys.dm_exec_query_stats qs LEFT JOIN sys.dm_exec_procedure_stats ps - ON qs.sql_handle = ps.sql_handle + ON qs.plan_handle = ps.plan_handle CROSS APPLY sys.dm_exec_plan_attributes(qs.plan_handle) pa WHERE pa.attribute = N'dbid' AND qs.query_plan_hash <> 0x0000000000000000 From a79a9ddd46a68f055ef96e656e8676bc41ccf8e4 Mon Sep 17 00:00:00 2001 From: PasswordIsMyPassword Date: Sat, 29 Jul 2023 18:04:09 -0500 Subject: [PATCH 23/30] #3314 update to --- sp_BlitzCache.sql | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sp_BlitzCache.sql b/sp_BlitzCache.sql index aa4d6450..162cfa99 100644 --- a/sp_BlitzCache.sql +++ b/sp_BlitzCache.sql @@ -1616,8 +1616,7 @@ WITH total_plans AS SELECT COUNT_BIG(qs.query_plan_hash) AS duplicate_plan_hashes FROM sys.dm_exec_query_stats qs - LEFT JOIN sys.dm_exec_procedure_stats ps - ON qs.plan_handle = ps.plan_handle + LEFT JOIN sys.dm_exec_procedure_stats ps ON qs.plan_handle = ps.plan_handle CROSS APPLY sys.dm_exec_plan_attributes(qs.plan_handle) pa WHERE pa.attribute = N'dbid' AND qs.query_plan_hash <> 0x0000000000000000 From 5252cdfcc60f22936fee10c731e89894c0bb9314 Mon Sep 17 00:00:00 2001 From: MDPenguin <122059580+mdpenguin@users.noreply.github.com> Date: Wed, 9 Aug 2023 12:57:18 -0400 Subject: [PATCH 24/30] Update PercentMemoryGrantUsed in sp_BlitzCache.sql Update to calculate as total_used_grant_kb / total_grant_kb. Fixes #3313. --- sp_BlitzCache.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp_BlitzCache.sql b/sp_BlitzCache.sql index 11600c64..0bf0920d 100644 --- a/sp_BlitzCache.sql +++ b/sp_BlitzCache.sql @@ -2324,7 +2324,7 @@ BEGIN max_grant_kb AS MaxGrantKB, min_used_grant_kb AS MinUsedGrantKB, max_used_grant_kb AS MaxUsedGrantKB, - CAST(ISNULL(NULLIF(( max_used_grant_kb * 1.00 ), 0) / NULLIF(min_grant_kb, 0), 0) * 100. AS MONEY) AS PercentMemoryGrantUsed, + CAST(ISNULL(NULLIF(( total_used_grant_kb * 1.00 ), 0) / NULLIF(total_grant_kb, 0), 0) * 100. AS MONEY) AS PercentMemoryGrantUsed, CAST(ISNULL(NULLIF(( total_grant_kb * 1. ), 0) / NULLIF(execution_count, 0), 0) AS MONEY) AS AvgMaxMemoryGrant, '; END; ELSE From 2f3e67365e2dae0a985f9ad6d6a73cf8138abf40 Mon Sep 17 00:00:00 2001 From: Brent Ozar Date: Thu, 10 Aug 2023 14:48:37 -0700 Subject: [PATCH 25/30] Versions - add 2022 CU6, CU7 --- SqlServerVersions.sql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/SqlServerVersions.sql b/SqlServerVersions.sql index 60c0b31e..0b5f69ae 100644 --- a/SqlServerVersions.sql +++ b/SqlServerVersions.sql @@ -41,6 +41,8 @@ DELETE FROM dbo.SqlServerVersions; INSERT INTO dbo.SqlServerVersions (MajorVersionNumber, MinorVersionNumber, Branch, [Url], ReleaseDate, MainstreamSupportEndDate, ExtendedSupportEndDate, MajorVersionName, MinorVersionName) VALUES + (16, 4065, 'CU7', 'https://support.microsoft.com/en-us/help/5028743', '2023-08-10', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'Cumulative Update 7'), + (16, 4055, 'CU6', 'https://support.microsoft.com/en-us/help/5027505', '2023-07-13', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'Cumulative Update 6'), (16, 4045, 'CU5', 'https://support.microsoft.com/en-us/help/5026806', '2023-06-15', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'Cumulative Update 5'), (16, 4035, 'CU4', 'https://support.microsoft.com/en-us/help/5026717', '2023-05-11', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'Cumulative Update 4'), (16, 4025, 'CU3', 'https://support.microsoft.com/en-us/help/5024396', '2023-04-13', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'Cumulative Update 3'), From d63983575f4efa4ed61775668dd78429beff0268 Mon Sep 17 00:00:00 2001 From: Brent Ozar Date: Tue, 15 Aug 2023 10:38:30 -0700 Subject: [PATCH 26/30] Update sp_Blitz.sql Moved the db_perms table population out of the non-SA section, removed the filter for only system databases. --- sp_Blitz.sql | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/sp_Blitz.sql b/sp_Blitz.sql index 7e6eaae9..548fde1d 100644 --- a/sp_Blitz.sql +++ b/sp_Blitz.sql @@ -207,6 +207,19 @@ AS permission_name sysname ); + INSERT + @db_perms + ( + database_name, + permission_name + ) + SELECT + database_name = + DB_NAME(d.database_id), + fmp.permission_name + FROM sys.databases AS d + CROSS APPLY fn_my_permissions(d.name, 'DATABASE') AS fmp + WHERE fmp.permission_name = N'SELECT' /*Databases where we don't have read permissions*/ /* End of declarations for First Responder Kit consistency check:*/ ; @@ -223,7 +236,9 @@ AS ) ) = 0 BEGIN - SET @sa = 0; /*Setting this to 0 to skip DBCC COMMANDS*/ + IF @Debug IN (1, 2) RAISERROR('User not SA, checking permissions', 0, 1) WITH NOWAIT; + + SET @sa = 0; /*Setting this to 0 to skip DBCC COMMANDS*/ IF NOT EXISTS ( @@ -292,20 +307,6 @@ AS SET @SkipValidateLogins = 1; END; /*Need execute on sp_validatelogins*/ - INSERT - @db_perms - ( - database_name, - permission_name - ) - SELECT - database_name = - DB_NAME(d.database_id), - fmp.permission_name - FROM sys.databases AS d - CROSS APPLY fn_my_permissions(d.name, 'DATABASE') AS fmp - WHERE fmp.permission_name = N'SELECT' - AND d.database_id < 5; /*Databases where we don't have read permissions*/ END; SET @crlf = NCHAR(13) + NCHAR(10); @@ -469,43 +470,43 @@ AS INSERT #SkipChecks (DatabaseName, CheckID, ServerName) SELECT v.* - FROM (VALUES(NULL, NULL, 29)) AS v (DatabaseName, CheckID, ServerName) /*Looks for user tables in model*/ + FROM (VALUES(NULL, 29, NULL)) AS v (DatabaseName, CheckID, ServerName) /*Looks for user tables in model*/ WHERE NOT EXISTS (SELECT 1/0 FROM @db_perms AS dp WHERE dp.database_name = 'model'); INSERT #SkipChecks (DatabaseName, CheckID, ServerName) SELECT v.* - FROM (VALUES(NULL, NULL, 68)) AS v (DatabaseName, CheckID, ServerName) /*DBCC command*/ + FROM (VALUES(NULL, 68, NULL)) AS v (DatabaseName, CheckID, ServerName) /*DBCC command*/ WHERE @sa = 0; INSERT #SkipChecks (DatabaseName, CheckID, ServerName) SELECT v.* - FROM (VALUES(NULL, NULL, 69)) AS v (DatabaseName, CheckID, ServerName) /*DBCC command*/ + FROM (VALUES(NULL, 69, NULL)) AS v (DatabaseName, CheckID, ServerName) /*DBCC command*/ WHERE @sa = 0; INSERT #SkipChecks (DatabaseName, CheckID, ServerName) SELECT v.* - FROM (VALUES(NULL, NULL, 92)) AS v (DatabaseName, CheckID, ServerName) /*xp_fixeddrives*/ + FROM (VALUES(NULL, 92, NULL)) AS v (DatabaseName, CheckID, ServerName) /*xp_fixeddrives*/ WHERE @SkipXPFixedDrives = 1; INSERT #SkipChecks (DatabaseName, CheckID, ServerName) SELECT v.* - FROM (VALUES(NULL, NULL, 211)) AS v (DatabaseName, CheckID, ServerName) /*xp_regread*/ + FROM (VALUES(NULL, 211, NULL)) AS v (DatabaseName, CheckID, ServerName) /*xp_regread*/ WHERE @SkipXPRegRead = 1; INSERT #SkipChecks (DatabaseName, CheckID, ServerName) SELECT v.* - FROM (VALUES(NULL, NULL, 212)) AS v (DatabaseName, CheckID, ServerName) /*xp_regread*/ + FROM (VALUES(NULL, 212, NULL)) AS v (DatabaseName, CheckID, ServerName) /*xp_regread*/ WHERE @SkipXPCMDShell = 1; INSERT #SkipChecks (DatabaseName, CheckID, ServerName) SELECT v.* - FROM (VALUES(NULL, NULL, 2301)) AS v (DatabaseName, CheckID, ServerName) /*sp_validatelogins*/ + FROM (VALUES(NULL, 2301, NULL)) AS v (DatabaseName, CheckID, ServerName) /*sp_validatelogins*/ WHERE @SkipValidateLogins = 1 IF(OBJECT_ID('tempdb..#InvalidLogins') IS NOT NULL) From 1d86feedeedc93a06f2647b06edb01ddb79b6080 Mon Sep 17 00:00:00 2001 From: sqljared Date: Thu, 17 Aug 2023 21:19:21 -0400 Subject: [PATCH 27/30] PSPO update for sp_BlitzQueryStore Detects if the database_scoped_configurations for PSPO is present and enabled; adds logic to include variant queries when @StoredProcName filter is used. --- sp_BlitzQueryStore.sql | 53 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/sp_BlitzQueryStore.sql b/sp_BlitzQueryStore.sql index 43171140..41c325e8 100644 --- a/sp_BlitzQueryStore.sql +++ b/sp_BlitzQueryStore.sql @@ -330,6 +330,28 @@ SET @msg = N'New query_store_runtime_stats columns ' + CASE @new_columns END; RAISERROR(@msg, 0, 1) WITH NOWAIT; +/* +This section determines if Parameter Sensitive Plan Optimization is enabled on SQL Server 2022+. +*/ + +RAISERROR('Checking for Parameter Sensitive Plan Optimization ', 0, 1) WITH NOWAIT; + +DECLARE @pspo_out BIT, + @pspo_enabled BIT, + @pspo_sql NVARCHAR(MAX) = N'SELECT @i_out = CONVERT(bit,dsc.value) + FROM ' + QUOTENAME(@DatabaseName) + N'.sys.database_scoped_configurations dsc + WHERE dsc.name = ''PARAMETER_SENSITIVE_PLAN_OPTIMIZATION'';', + @pspo_params NVARCHAR(MAX) = N'@i_out INT OUTPUT'; + +EXEC sys.sp_executesql @pspo_sql, @pspo_params, @i_out = @pspo_out OUTPUT; + +SET @pspo_enabled = CASE WHEN @pspo_out = 1 THEN 1 ELSE 0 END; + +SET @msg = N'Parameter Sensitive Plan Optimization ' + CASE @pspo_enabled + WHEN 0 THEN N' not enabled, skipping.' + WHEN 1 THEN N' enabled, will analyze.' + END; +RAISERROR(@msg, 0, 1) WITH NOWAIT; /* These are the temp tables we use @@ -1033,10 +1055,33 @@ IF @MinimumExecutionCount IS NOT NULL --You care about stored proc names IF @StoredProcName IS NOT NULL - BEGIN - RAISERROR(N'Setting stored proc filter', 0, 1) WITH NOWAIT; - SET @sql_where += N' AND object_name(qsq.object_id, DB_ID(' + QUOTENAME(@DatabaseName, '''') + N')) = @sp_StoredProcName - '; + BEGIN + + IF (@pspo_enabled = 1) + BEGIN + RAISERROR(N'Setting stored proc filter, PSPO enabled', 0, 1) WITH NOWAIT; + /* If PSPO is enabled, the object_id for a variant query would be 0. To include it, we check whether the object_id = 0 query + is a variant query, and whether it's parent query belongs to @sp_StoredProcName. */ + SET @sql_where += N' AND (object_name(qsq.object_id, DB_ID(' + QUOTENAME(@DatabaseName, '''') + N')) = @sp_StoredProcName + OR (qsq.object_id = 0 + AND EXISTS( + SELECT 1 + FROM ' + QUOTENAME(@DatabaseName) + N'.sys.query_store_query_variant vr + JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.query_store_query pqsq + ON pqsq.query_id = vr.parent_query_id + WHERE + vr.query_variant_query_id = qsq.query_id + AND object_name(pqsq.object_id, DB_ID(' + QUOTENAME(@DatabaseName, '''') + N')) = @sp_StoredProcName + ) + )) + '; + END + ELSE + BEGIN + RAISERROR(N'Setting stored proc filter', 0, 1) WITH NOWAIT; + SET @sql_where += N' AND object_name(qsq.object_id, DB_ID(' + QUOTENAME(@DatabaseName, '''') + N')) = @sp_StoredProcName + '; + END END; --I will always love you, but hopefully this query will eventually end From 3527872f27c59b4850f52a27b0750c6e8806ca49 Mon Sep 17 00:00:00 2001 From: Greg Dodd Date: Fri, 18 Aug 2023 12:19:51 +1000 Subject: [PATCH 28/30] Fixing output message to show correct parameter --- sp_DatabaseRestore.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp_DatabaseRestore.sql b/sp_DatabaseRestore.sql index 06968cad..db32e410 100755 --- a/sp_DatabaseRestore.sql +++ b/sp_DatabaseRestore.sql @@ -1342,7 +1342,7 @@ IF (@LogRecoveryOption = N'') IF (@StopAt IS NOT NULL) BEGIN - IF @Execute = 'Y' OR @Debug = 1 RAISERROR('@OnlyLogsAfter is NOT NULL, deleting from @FileList', 0, 1) WITH NOWAIT; + IF @Execute = 'Y' OR @Debug = 1 RAISERROR('@StopAt is NOT NULL, deleting from @FileList', 0, 1) WITH NOWAIT; IF LEN(@StopAt) <> 14 OR PATINDEX('%[^0-9]%', @StopAt) > 0 BEGIN From 6081f4d1451c86e7f61c325d7db8660258f92254 Mon Sep 17 00:00:00 2001 From: sqljared Date: Thu, 17 Aug 2023 22:20:59 -0400 Subject: [PATCH 29/30] Update proc_or_function_name for PSPO in sp_BlitzQueryStore If PSPO is enabled, attempt to populate proc_or_function_name based on a parent query. --- sp_BlitzQueryStore.sql | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/sp_BlitzQueryStore.sql b/sp_BlitzQueryStore.sql index 41c325e8..70993ce3 100644 --- a/sp_BlitzQueryStore.sql +++ b/sp_BlitzQueryStore.sql @@ -2315,6 +2315,30 @@ EXEC sys.sp_executesql @stmt = @sql_select, @sp_Top = @Top, @sp_StartDate = @StartDate, @sp_EndDate = @EndDate, @sp_MinimumExecutionCount = @MinimumExecutionCount, @sp_MinDuration = @duration_filter_ms, @sp_StoredProcName = @StoredProcName, @sp_PlanIdFilter = @PlanIdFilter, @sp_QueryIdFilter = @QueryIdFilter; +/*If PSPO is enabled, get procedure names for variant queries.*/ +IF (@pspo_enabled = 1) +BEGIN + DECLARE + @pspo_names NVARCHAR(MAX) = ''; + + SET @pspo_names = + 'UPDATE wm + SET + wm.proc_or_function_name = + QUOTENAME(object_schema_name(qsq.object_id, DB_ID(' + QUOTENAME(@DatabaseName, '''') + N'))) + ''.'' + + QUOTENAME(object_name(qsq.object_id, DB_ID(' + QUOTENAME(@DatabaseName, '''') + N'))) + FROM #working_metrics wm + JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.query_store_query_variant AS vr + ON vr.query_variant_query_id = wm.query_id + JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.query_store_query AS qsq + ON qsq.query_id = vr.parent_query_id + AND qsq.object_id > 0 + WHERE + wm.proc_or_function_name IS NULL;' + + EXEC sys.sp_executesql @pspo_names; +END; + /*This just helps us classify our queries*/ UPDATE #working_metrics From ef4670f0bc59b24c57db512b8e4ba8bfbf4455de Mon Sep 17 00:00:00 2001 From: Brent Ozar Date: Sun, 20 Aug 2023 08:08:34 -0700 Subject: [PATCH 30/30] 2023-08-20 release Bumping dates and version numbers, adding SQL 2019 CU22, generated install scripts. --- Install-All-Scripts.sql | 983 +++++++++++++++++++----- Install-Core-Blitz-No-Query-Store.sql | 951 ++++++++++++++++++----- Install-Core-Blitz-With-Query-Store.sql | 953 ++++++++++++++++++----- SqlServerVersions.sql | 1 + sp_AllNightLog.sql | 2 +- sp_AllNightLog_Setup.sql | 2 +- sp_Blitz.sql | 2 +- sp_BlitzAnalysis.sql | 2 +- sp_BlitzBackups.sql | 2 +- sp_BlitzCache.sql | 2 +- sp_BlitzFirst.sql | 2 +- sp_BlitzInMemoryOLTP.sql | 2 +- sp_BlitzIndex.sql | 2 +- sp_BlitzLock.sql | 2 +- sp_BlitzQueryStore.sql | 2 +- sp_BlitzWho.sql | 2 +- sp_DatabaseRestore.sql | 2 +- sp_ineachdb.sql | 2 +- 18 files changed, 2375 insertions(+), 541 deletions(-) diff --git a/Install-All-Scripts.sql b/Install-All-Scripts.sql index 054ff3f1..ca4313b1 100644 --- a/Install-All-Scripts.sql +++ b/Install-All-Scripts.sql @@ -38,7 +38,7 @@ SET STATISTICS XML OFF; BEGIN; -SELECT @Version = '8.15', @VersionDate = '20230613'; +SELECT @Version = '8.16', @VersionDate = '20230820'; IF(@VersionCheckMode = 1) BEGIN @@ -1375,7 +1375,7 @@ SET STATISTICS XML OFF; BEGIN; -SELECT @Version = '8.15', @VersionDate = '20230613'; +SELECT @Version = '8.16', @VersionDate = '20230820'; IF(@VersionCheckMode = 1) BEGIN @@ -2900,7 +2900,7 @@ AS SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; - SELECT @Version = '8.15', @VersionDate = '20230613'; + SELECT @Version = '8.16', @VersionDate = '20230820'; SET @OutputType = UPPER(@OutputType); IF(@VersionCheckMode = 1) @@ -3048,11 +3048,129 @@ AS ,@CurrentComponentVersionCheckModeOK BIT ,@canExitLoop BIT ,@frkIsConsistent BIT - ,@NeedToTurnNumericRoundabortBackOn BIT; + ,@NeedToTurnNumericRoundabortBackOn BIT + ,@sa bit = 1 + ,@SUSER_NAME sysname = SUSER_SNAME() + ,@SkipDBCC bit = 0 + ,@SkipTrace bit = 0 + ,@SkipXPRegRead bit = 0 + ,@SkipXPFixedDrives bit = 0 + ,@SkipXPCMDShell bit = 0 + ,@SkipMaster bit = 0 + ,@SkipMSDB bit = 0 + ,@SkipModel bit = 0 + ,@SkipTempDB bit = 0 + ,@SkipValidateLogins bit = 0; + + DECLARE + @db_perms table + ( + database_name sysname, + permission_name sysname + ); + + INSERT + @db_perms + ( + database_name, + permission_name + ) + SELECT + database_name = + DB_NAME(d.database_id), + fmp.permission_name + FROM sys.databases AS d + CROSS APPLY fn_my_permissions(d.name, 'DATABASE') AS fmp + WHERE fmp.permission_name = N'SELECT' /*Databases where we don't have read permissions*/ /* End of declarations for First Responder Kit consistency check:*/ ; + /*Starting permissions checks here, but only if we're not a sysadmin*/ + IF + ( + SELECT + sa = + ISNULL + ( + IS_SRVROLEMEMBER(N'sysadmin'), + 0 + ) + ) = 0 + BEGIN + IF @Debug IN (1, 2) RAISERROR('User not SA, checking permissions', 0, 1) WITH NOWAIT; + + SET @sa = 0; /*Setting this to 0 to skip DBCC COMMANDS*/ + + IF NOT EXISTS + ( + SELECT + 1/0 + FROM sys.fn_my_permissions(NULL, NULL) AS fmp + WHERE fmp.permission_name = N'VIEW SERVER STATE' + ) + BEGIN + RAISERROR('The user %s does not have VIEW SERVER STATE permissions.', 0, 11, @SUSER_NAME) WITH NOWAIT; + RETURN; + END; /*If we don't have this, we can't do anything at all.*/ + + IF NOT EXISTS + ( + SELECT + 1/0 + FROM fn_my_permissions(N'sys.traces', N'OBJECT') AS fmp + WHERE fmp.permission_name = N'ALTER' + ) + BEGIN + SET @SkipTrace = 1; + END; /*We need this permission to execute trace stuff, apparently*/ + + IF NOT EXISTS + ( + SELECT + 1/0 + FROM fn_my_permissions(N'xp_regread', N'OBJECT') AS fmp + WHERE fmp.permission_name = N'EXECUTE' + ) + BEGIN + SET @SkipXPRegRead = 1; + END; /*Need execute on xp_regread*/ + + IF NOT EXISTS + ( + SELECT + 1/0 + FROM fn_my_permissions(N'xp_fixeddrives', N'OBJECT') AS fmp + WHERE fmp.permission_name = N'EXECUTE' + ) + BEGIN + SET @SkipXPFixedDrives = 1; + END; /*Need execute on xp_fixeddrives*/ + + IF NOT EXISTS + ( + SELECT + 1/0 + FROM fn_my_permissions(N'xp_cmdshell', N'OBJECT') AS fmp + WHERE fmp.permission_name = N'EXECUTE' + ) + BEGIN + SET @SkipXPCMDShell = 1; + END; /*Need execute on xp_cmdshell*/ + + IF NOT EXISTS + ( + SELECT + 1/0 + FROM fn_my_permissions(N'sp_validatelogins', N'OBJECT') AS fmp + WHERE fmp.permission_name = N'EXECUTE' + ) + BEGIN + SET @SkipValidateLogins = 1; + END; /*Need execute on sp_validatelogins*/ + + END; + SET @crlf = NCHAR(13) + NCHAR(10); SET @ResultText = 'sp_Blitz Results: ' + @crlf; @@ -3193,6 +3311,66 @@ AS OR LOWER(d.name) IN ('dbatools', 'dbadmin', 'dbmaintenance')) OPTION(RECOMPILE); + /*Skip checks for database where we don't have read permissions*/ + INSERT INTO + #SkipChecks + ( + DatabaseName + ) + SELECT + DB_NAME(d.database_id) + FROM sys.databases AS d + WHERE NOT EXISTS + ( + SELECT + 1/0 + FROM @db_perms AS dp + WHERE dp.database_name = DB_NAME(d.database_id) + ); + + /*Skip individial checks where we don't have permissions*/ + INSERT #SkipChecks (DatabaseName, CheckID, ServerName) + SELECT + v.* + FROM (VALUES(NULL, 29, NULL)) AS v (DatabaseName, CheckID, ServerName) /*Looks for user tables in model*/ + WHERE NOT EXISTS (SELECT 1/0 FROM @db_perms AS dp WHERE dp.database_name = 'model'); + + INSERT #SkipChecks (DatabaseName, CheckID, ServerName) + SELECT + v.* + FROM (VALUES(NULL, 68, NULL)) AS v (DatabaseName, CheckID, ServerName) /*DBCC command*/ + WHERE @sa = 0; + + INSERT #SkipChecks (DatabaseName, CheckID, ServerName) + SELECT + v.* + FROM (VALUES(NULL, 69, NULL)) AS v (DatabaseName, CheckID, ServerName) /*DBCC command*/ + WHERE @sa = 0; + + INSERT #SkipChecks (DatabaseName, CheckID, ServerName) + SELECT + v.* + FROM (VALUES(NULL, 92, NULL)) AS v (DatabaseName, CheckID, ServerName) /*xp_fixeddrives*/ + WHERE @SkipXPFixedDrives = 1; + + INSERT #SkipChecks (DatabaseName, CheckID, ServerName) + SELECT + v.* + FROM (VALUES(NULL, 211, NULL)) AS v (DatabaseName, CheckID, ServerName) /*xp_regread*/ + WHERE @SkipXPRegRead = 1; + + INSERT #SkipChecks (DatabaseName, CheckID, ServerName) + SELECT + v.* + FROM (VALUES(NULL, 212, NULL)) AS v (DatabaseName, CheckID, ServerName) /*xp_regread*/ + WHERE @SkipXPCMDShell = 1; + + INSERT #SkipChecks (DatabaseName, CheckID, ServerName) + SELECT + v.* + FROM (VALUES(NULL, 2301, NULL)) AS v (DatabaseName, CheckID, ServerName) /*sp_validatelogins*/ + WHERE @SkipValidateLogins = 1 + IF(OBJECT_ID('tempdb..#InvalidLogins') IS NOT NULL) BEGIN EXEC sp_executesql N'DROP TABLE #InvalidLogins;'; @@ -3234,7 +3412,8 @@ AS SELECT @IsWindowsOperatingSystem = 1 ; END; - IF NOT EXISTS ( SELECT 1 + + IF NOT EXISTS ( SELECT 1 FROM #SkipChecks WHERE DatabaseName IS NULL AND CheckID = 106 ) AND (select convert(int,value_in_use) from sys.configurations where name = 'default trace enabled' ) = 1 @@ -7020,53 +7199,56 @@ AS /* First, let's check that there aren't any issues with the trace files */ BEGIN TRY - - INSERT INTO #fnTraceGettable - ( TextData , - DatabaseName , - EventClass , - Severity , - StartTime , - EndTime , - Duration , - NTUserName , - NTDomainName , - HostName , - ApplicationName , - LoginName , - DBUserName - ) - SELECT TOP 20000 - CONVERT(NVARCHAR(4000),t.TextData) , - t.DatabaseName , - t.EventClass , - t.Severity , - t.StartTime , - t.EndTime , - t.Duration , - t.NTUserName , - t.NTDomainName , - t.HostName , - t.ApplicationName , - t.LoginName , - t.DBUserName - FROM sys.fn_trace_gettable(@base_tracefilename, DEFAULT) t - WHERE - ( - t.EventClass = 22 - AND t.Severity >= 17 - AND t.StartTime > DATEADD(dd, -30, GETDATE()) - ) - OR - ( - t.EventClass IN (92, 93) - AND t.StartTime > DATEADD(dd, -30, GETDATE()) - AND t.Duration > 15000000 - ) - OR - ( - t.EventClass IN (94, 95, 116) - ) + + IF @SkipTrace = 0 + BEGIN + INSERT INTO #fnTraceGettable + ( TextData , + DatabaseName , + EventClass , + Severity , + StartTime , + EndTime , + Duration , + NTUserName , + NTDomainName , + HostName , + ApplicationName , + LoginName , + DBUserName + ) + SELECT TOP 20000 + CONVERT(NVARCHAR(4000),t.TextData) , + t.DatabaseName , + t.EventClass , + t.Severity , + t.StartTime , + t.EndTime , + t.Duration , + t.NTUserName , + t.NTDomainName , + t.HostName , + t.ApplicationName , + t.LoginName , + t.DBUserName + FROM sys.fn_trace_gettable(@base_tracefilename, DEFAULT) t + WHERE + ( + t.EventClass = 22 + AND t.Severity >= 17 + AND t.StartTime > DATEADD(dd, -30, GETDATE()) + ) + OR + ( + t.EventClass IN (92, 93) + AND t.StartTime > DATEADD(dd, -30, GETDATE()) + AND t.Duration > 15000000 + ) + OR + ( + t.EventClass IN (94, 95, 116) + ) + END; SET @TraceFileIssue = 0 @@ -9441,10 +9623,10 @@ IF @ProductVersionMajor >= 10 DatabaseName FROM #SkipChecks WHERE CheckID IS NULL OR CheckID = 19) - AND is_published = 1 + AND (is_published = 1 OR is_subscribed = 1 OR is_merge_published = 1 - OR is_distributor = 1; + OR is_distributor = 1); /* Method B: check subscribers for MSreplication_objects tables */ EXEC dbo.sp_MSforeachdb 'USE [?]; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; @@ -12599,7 +12781,7 @@ AS SET NOCOUNT ON; SET STATISTICS XML OFF; -SELECT @Version = '8.15', @VersionDate = '20230613'; +SELECT @Version = '8.16', @VersionDate = '20230820'; IF(@VersionCheckMode = 1) BEGIN @@ -13477,7 +13659,7 @@ AS SET STATISTICS XML OFF; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; - SELECT @Version = '8.15', @VersionDate = '20230613'; + SELECT @Version = '8.16', @VersionDate = '20230820'; IF(@VersionCheckMode = 1) BEGIN @@ -15259,7 +15441,7 @@ SET NOCOUNT ON; SET STATISTICS XML OFF; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -SELECT @Version = '8.15', @VersionDate = '20230613'; +SELECT @Version = '8.16', @VersionDate = '20230820'; SET @OutputType = UPPER(@OutputType); IF(@VersionCheckMode = 1) @@ -16557,6 +16739,7 @@ BEGIN RAISERROR('Checking for Read intent databases to exclude',0,0) WITH NOWAIT; EXEC('INSERT INTO #ReadableDBs (database_id) SELECT DBs.database_id FROM sys.databases DBs INNER JOIN sys.availability_replicas Replicas ON DBs.replica_id = Replicas.replica_id WHERE replica_server_name NOT IN (SELECT DISTINCT primary_replica FROM sys.dm_hadr_availability_group_states States) AND Replicas.secondary_role_allow_connections_desc = ''READ_ONLY'' AND replica_server_name = @@SERVERNAME OPTION (RECOMPILE);'); + EXEC('INSERT INTO #ReadableDBs VALUES (32767) ;'); -- Exclude internal resource database as well END RAISERROR(N'Checking plan cache age', 0, 1) WITH NOWAIT; @@ -16593,10 +16776,10 @@ WITH total_plans AS SELECT COUNT_BIG(qs.query_plan_hash) AS duplicate_plan_hashes FROM sys.dm_exec_query_stats qs - LEFT JOIN sys.dm_exec_procedure_stats ps - ON qs.sql_handle = ps.sql_handle + LEFT JOIN sys.dm_exec_procedure_stats ps ON qs.plan_handle = ps.plan_handle CROSS APPLY sys.dm_exec_plan_attributes(qs.plan_handle) pa WHERE pa.attribute = N'dbid' + AND pa.value <> 32767 /*Omit Resource database-based queries, we're not going to "fix" them no matter what. Addresses #3314*/ AND qs.query_plan_hash <> 0x0000000000000000 GROUP BY /* qs.query_plan_hash, BGO 20210524 commenting this out to fix #2909 */ @@ -17301,7 +17484,7 @@ BEGIN max_grant_kb AS MaxGrantKB, min_used_grant_kb AS MinUsedGrantKB, max_used_grant_kb AS MaxUsedGrantKB, - CAST(ISNULL(NULLIF(( max_used_grant_kb * 1.00 ), 0) / NULLIF(min_grant_kb, 0), 0) * 100. AS MONEY) AS PercentMemoryGrantUsed, + CAST(ISNULL(NULLIF(( total_used_grant_kb * 1.00 ), 0) / NULLIF(total_grant_kb, 0), 0) * 100. AS MONEY) AS PercentMemoryGrantUsed, CAST(ISNULL(NULLIF(( total_grant_kb * 1. ), 0) / NULLIF(execution_count, 0), 0) AS MONEY) AS AvgMaxMemoryGrant, '; END; ELSE @@ -22577,7 +22760,7 @@ SET NOCOUNT ON; SET STATISTICS XML OFF; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -SELECT @Version = '8.15', @VersionDate = '20230613'; +SELECT @Version = '8.16', @VersionDate = '20230820'; SET @OutputType = UPPER(@OutputType); IF(@VersionCheckMode = 1) @@ -25487,7 +25670,8 @@ BEGIN + CASE WHEN @ShowPartitionRanges = 1 THEN N' COALESCE(range_start_op + '' '' + range_start + '' '', '''') + COALESCE(range_end_op + '' '' + range_end, '''') AS partition_range, ' ELSE N' ' END + N' row_group_id, total_rows, deleted_rows, ' + @ColumnList - + CASE WHEN @ShowPartitionRanges = 1 THEN N' + + CASE WHEN @ShowPartitionRanges = 1 THEN N' , + state_desc, trim_reason_desc, transition_to_compressed_state_desc, has_vertipaq_optimization FROM ( SELECT column_name, partition_number, row_group_id, total_rows, deleted_rows, details, range_start_op, @@ -25497,10 +25681,12 @@ BEGIN range_end_op, CASE WHEN format_type IS NULL THEN CAST(range_end_value AS NVARCHAR(4000)) - ELSE CONVERT(NVARCHAR(4000), range_end_value, format_type) END range_end' ELSE N' ' END + N' + ELSE CONVERT(NVARCHAR(4000), range_end_value, format_type) END range_end' ELSE N' ' END + N', + state_desc, trim_reason_desc, transition_to_compressed_state_desc, has_vertipaq_optimization FROM ( SELECT c.name AS column_name, p.partition_number, rg.row_group_id, rg.total_rows, rg.deleted_rows, - details = CAST(seg.min_data_id AS VARCHAR(20)) + '' to '' + CAST(seg.max_data_id AS VARCHAR(20)) + '', '' + CAST(CAST((seg.on_disk_size / 1024.0 / 1024) AS DECIMAL(18,0)) AS VARCHAR(20)) + '' MB''' + phys.state_desc, phys.trim_reason_desc, phys.transition_to_compressed_state_desc, phys.has_vertipaq_optimization, + details = CAST(seg.min_data_id AS VARCHAR(20)) + '' to '' + CAST(seg.max_data_id AS VARCHAR(20)) + '', '' + CAST(CAST(((COALESCE(d.on_disk_size,0) + COALESCE(seg.on_disk_size,0)) / 1024.0 / 1024) AS DECIMAL(18,0)) AS VARCHAR(20)) + '' MB''' + CASE WHEN @ShowPartitionRanges = 1 THEN N', CASE WHEN pp.system_type_id IN (40, 41, 42, 43, 58, 61) THEN 126 @@ -25514,7 +25700,8 @@ BEGIN FROM ' + QUOTENAME(@DatabaseName) + N'.sys.column_store_row_groups rg INNER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.columns c ON rg.object_id = c.object_id INNER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.partitions p ON rg.object_id = p.object_id AND rg.partition_number = p.partition_number - INNER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.index_columns ic on ic.column_id = c.column_id AND ic.object_id = c.object_id AND ic.index_id = p.index_id ' + CASE WHEN @ShowPartitionRanges = 1 THEN N' + INNER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.index_columns ic on ic.column_id = c.column_id AND ic.object_id = c.object_id AND ic.index_id = p.index_id + LEFT OUTER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.dm_db_column_store_row_group_physical_stats phys ON rg.row_group_id = phys.row_group_id AND rg.object_id = phys.object_id AND rg.partition_number = phys.partition_number AND p.index_id = phys.index_id ' + CASE WHEN @ShowPartitionRanges = 1 THEN N' LEFT OUTER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.indexes i ON i.object_id = rg.object_id AND i.index_id = rg.index_id LEFT OUTER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.partition_schemes ps ON ps.data_space_id = i.data_space_id LEFT OUTER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.partition_functions pf ON pf.function_id = ps.function_id @@ -25522,6 +25709,7 @@ BEGIN LEFT OUTER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.partition_range_values prvs ON prvs.function_id = pf.function_id AND prvs.boundary_id = p.partition_number - 1 LEFT OUTER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.partition_range_values prve ON prve.function_id = pf.function_id AND prve.boundary_id = p.partition_number ' ELSE N' ' END + N' LEFT OUTER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.column_store_segments seg ON p.partition_id = seg.partition_id AND ic.index_column_id = seg.column_id AND rg.row_group_id = seg.segment_id + LEFT OUTER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.column_store_dictionaries d ON p.hobt_id = d.hobt_id AND c.column_id = d.column_id AND seg.secondary_dictionary_id = d.dictionary_id WHERE rg.object_id = @ObjectID AND rg.state IN (1, 2, 3) AND c.name IN ( ' + @ColumnListWithApostrophes + N')' @@ -25559,6 +25747,9 @@ BEGIN RAISERROR(N'Done visualizing columnstore index contents.', 0,1) WITH NOWAIT; END + IF @ShowColumnstoreOnly = 1 + RETURN; + END; /* IF @TableName IS NOT NULL */ @@ -28754,7 +28945,7 @@ BEGIN SET NOCOUNT, XACT_ABORT ON; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; - SELECT @Version = '8.15', @VersionDate = '20230613'; + SELECT @Version = '8.16', @VersionDate = '20230820'; IF @VersionCheckMode = 1 BEGIN @@ -28898,7 +29089,11 @@ BEGIN @TargetSessionId int = 0, @FileName nvarchar(4000) = N'', @inputbuf_bom nvarchar(1) = CONVERT(nvarchar(1), 0x0a00, 0), - @deadlock_result nvarchar(MAX) = N''; + @deadlock_result nvarchar(MAX) = N'', + @StartDateOriginal datetime = @StartDate, + @EndDateOriginal datetime = @EndDate, + @StartDateUTC datetime, + @EndDateUTC datetime; /*Temporary objects used in the procedure*/ DECLARE @@ -28938,15 +29133,17 @@ BEGIN database_name nvarchar(256), object_name nvarchar(1000), finding_group nvarchar(100), - finding nvarchar(4000) + finding nvarchar(4000), + sort_order bigint ); /*Set these to some sane defaults if NULLs are passed in*/ /*Normally I'd hate this, but we RECOMPILE everything*/ + SELECT @StartDate = - CASE - WHEN @StartDate IS NULL + CASE + WHEN @StartDate IS NULL THEN DATEADD ( @@ -28957,18 +29154,25 @@ BEGIN SYSDATETIME(), GETUTCDATE() ), - ISNULL + DATEADD ( - @StartDate, - DATEADD - ( - DAY, - -7, - SYSDATETIME() - ) + DAY, + -7, + SYSDATETIME() ) ) - ELSE @StartDate + ELSE + DATEADD + ( + MINUTE, + DATEDIFF + ( + MINUTE, + SYSDATETIME(), + GETUTCDATE() + ), + @StartDate + ) END, @EndDate = CASE @@ -28983,15 +29187,62 @@ BEGIN SYSDATETIME(), GETUTCDATE() ), - ISNULL + SYSDATETIME() + ) + ELSE + DATEADD + ( + MINUTE, + DATEDIFF ( - @EndDate, - SYSDATETIME() - ) + MINUTE, + SYSDATETIME(), + GETUTCDATE() + ), + @EndDate ) - ELSE @EndDate END; + SELECT + @StartDateUTC = @StartDate, + @EndDateUTC = @EndDate; + + IF @Azure = 0 + BEGIN + IF NOT EXISTS + ( + SELECT + 1/0 + FROM sys.server_event_sessions AS ses + JOIN sys.dm_xe_sessions AS dxs + ON dxs.name = ses.name + WHERE ses.name = @EventSessionName + AND dxs.create_time IS NOT NULL + ) + BEGIN + RAISERROR('A session with the name %s does not exist or is not currently active.', 11, 1, @EventSessionName) WITH NOWAIT; + RETURN; + END; + END; + + IF @Azure = 1 + BEGIN + IF NOT EXISTS + ( + SELECT + 1/0 + FROM sys.database_event_sessions AS ses + JOIN sys.dm_xe_database_sessions AS dxs + ON dxs.name = ses.name + WHERE ses.name = @EventSessionName + AND dxs.create_time IS NOT NULL + ) + BEGIN + RAISERROR('A session with the name %s does not exist or is not currently active.', 11, 1, @EventSessionName) WITH NOWAIT; + RETURN; + END; + END; + IF @OutputDatabaseName IS NOT NULL BEGIN /*IF databaseName is set, do some sanity checks and put [] around def.*/ SET @d = CONVERT(varchar(40), GETDATE(), 109); @@ -29574,8 +29825,7 @@ BEGIN OR e.x.exist('@name[ .= "database_xml_deadlock_report"]') = 1 OR e.x.exist('@name[ .= "xml_deadlock_report_filtered"]') = 1 ) - AND e.x.exist('@timestamp[. >= sql:variable("@StartDate")]') = 1 - AND e.x.exist('@timestamp[. < sql:variable("@EndDate")]') = 1 + AND e.x.exist('@timestamp[. >= sql:variable("@StartDate") and .< sql:variable("@EndDate")]') = 1 OPTION(RECOMPILE); SET @d = CONVERT(varchar(40), GETDATE(), 109); @@ -29592,9 +29842,9 @@ BEGIN SET @d = CONVERT(varchar(40), GETDATE(), 109); RAISERROR('Inserting to #deadlock_data for event file data', 0, 1) WITH NOWAIT; - IF @Debug = 1 BEGIN SET STATISTICS XML ON; END; + IF @Debug = 1 BEGIN SET STATISTICS XML ON; END; - INSERT + INSERT #deadlock_data WITH(TABLOCKX) ( deadlock_xml @@ -29612,13 +29862,12 @@ BEGIN OR e.x.exist('@name[ .= "database_xml_deadlock_report"]') = 1 OR e.x.exist('@name[ .= "xml_deadlock_report_filtered"]') = 1 ) - AND e.x.exist('@timestamp[. >= sql:variable("@StartDate")]') = 1 - AND e.x.exist('@timestamp[. < sql:variable("@EndDate")]') = 1 + AND e.x.exist('@timestamp[. >= sql:variable("@StartDate") and .< sql:variable("@EndDate")]') = 1 OPTION(RECOMPILE); - IF @Debug = 1 BEGIN SET STATISTICS XML OFF; END; + IF @Debug = 1 BEGIN SET STATISTICS XML OFF; END; - SET @d = CONVERT(varchar(40), GETDATE(), 109); + SET @d = CONVERT(varchar(40), GETDATE(), 109); RAISERROR('Finished at %s', 0, 1, @d) WITH NOWAIT; END; @@ -29649,8 +29898,7 @@ BEGIN ) AS xml CROSS APPLY xml.deadlock_xml.nodes('/event') AS e(x) WHERE 1 = 1 - AND e.x.exist('@timestamp[. >= sql:variable("@StartDate")]') = 1 - AND e.x.exist('@timestamp[. < sql:variable("@EndDate")]') = 1 + AND e.x.exist('@timestamp[. >= sql:variable("@StartDate") and .< sql:variable("@EndDate")]') = 1 OPTION(RECOMPILE); INSERT @@ -29777,7 +30025,12 @@ BEGIN DATEADD ( MINUTE, - DATEDIFF(MINUTE, GETUTCDATE(), SYSDATETIME()), + DATEDIFF + ( + MINUTE, + GETUTCDATE(), + SYSDATETIME() + ), dd.event_date ), dd.victim_id, @@ -29835,6 +30088,7 @@ BEGIN FROM #deadlock_process AS dp CROSS APPLY dp.process_xml.nodes('//executionStack/frame') AS ca(dp) WHERE (ca.dp.exist('@procname[. = sql:variable("@StoredProcName")]') = 1 OR @StoredProcName IS NULL) + AND ca.dp.exist('@sqlhandle[ .= "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"]') = 0 OPTION(RECOMPILE); SET @d = CONVERT(varchar(40), GETDATE(), 109); @@ -29907,7 +30161,7 @@ BEGIN waiter_mode = w.l.value('@mode', 'nvarchar(256)'), owner_id = o.l.value('@id', 'nvarchar(256)'), owner_mode = o.l.value('@mode', 'nvarchar(256)'), - lock_type = CAST(N'OBJECT' AS NVARCHAR(100)) + lock_type = CAST(N'OBJECT' AS nvarchar(100)) INTO #deadlock_owner_waiter FROM ( @@ -30303,13 +30557,19 @@ BEGIN 32 ), step_id = - SUBSTRING - ( - dp.client_app, - CHARINDEX(N': Step ', dp.client_app) + LEN(N': Step '), - CHARINDEX(N')', dp.client_app, CHARINDEX(N': Step ', dp.client_app)) - - (CHARINDEX(N': Step ', dp.client_app) + LEN(N': Step ')) - ) + CASE + WHEN CHARINDEX(N': Step ', dp.client_app) > 0 + AND CHARINDEX(N')', dp.client_app, CHARINDEX(N': Step ', dp.client_app)) > 0 + THEN + SUBSTRING + ( + dp.client_app, + CHARINDEX(N': Step ', dp.client_app) + LEN(N': Step '), + CHARINDEX(N')', dp.client_app, CHARINDEX(N': Step ', dp.client_app)) - + (CHARINDEX(N': Step ', dp.client_app) + LEN(N': Step ')) + ) + ELSE dp.client_app + END FROM #deadlock_process AS dp WHERE dp.client_app LIKE N'SQLAgent - %' AND dp.client_app <> N'SQLAgent - Initial Boot Probe' @@ -30459,6 +30719,14 @@ BEGIN /*Begin checks based on parsed values*/ + /* + First, revert these back since we already converted the event data to local time, + and searches will break if we use the times converted over to UTC for the event data + */ + SELECT + @StartDate = @StartDateOriginal, + @EndDate = @EndDateOriginal; + /*Check 1 is deadlocks by database*/ SET @d = CONVERT(varchar(40), GETDATE(), 109); RAISERROR('Check 1 database deadlocks %s', 0, 1, @d) WITH NOWAIT; @@ -30470,7 +30738,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 1, @@ -30484,7 +30753,10 @@ BEGIN nvarchar(20), COUNT_BIG(DISTINCT dp.event_date) ) + - N' deadlocks.' + N' deadlocks.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT dp.event_date) DESC) FROM #deadlock_process AS dp WHERE 1 = 1 AND (dp.database_name = @DatabaseName OR @DatabaseName IS NULL) @@ -30509,7 +30781,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 2, @@ -30524,7 +30797,10 @@ BEGIN nvarchar(20), COUNT_BIG(DISTINCT dow.event_date) ) + - N' deadlock(s) between read queries and modification queries.' + N' deadlock(s) between read queries and modification queries.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT dow.event_date) DESC) FROM #deadlock_owner_waiter AS dow WHERE 1 = 1 AND dow.lock_mode IN @@ -30563,7 +30839,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 3, @@ -30582,7 +30859,10 @@ BEGIN nvarchar(20), COUNT_BIG(DISTINCT dow.event_date) ) + - N' deadlock(s).' + N' deadlock(s).', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT dow.event_date) DESC) FROM #deadlock_owner_waiter AS dow WHERE 1 = 1 AND (dow.database_id = @DatabaseId OR @DatabaseName IS NULL) @@ -30607,7 +30887,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 3, @@ -30621,7 +30902,10 @@ BEGIN nvarchar(20), COUNT_BIG(DISTINCT dow.event_date) ) + - N' deadlock(s).' + N' deadlock(s).', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT dow.event_date) DESC) FROM #deadlock_owner_waiter AS dow WHERE 1 = 1 AND (dow.database_id = @DatabaseId OR @DatabaseName IS NULL) @@ -30652,7 +30936,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 3, @@ -30666,7 +30951,10 @@ BEGIN nvarchar(20), COUNT_BIG(DISTINCT dow.event_date) ) + - N' deadlock(s).' + N' deadlock(s).', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT dow.event_date) DESC) FROM #deadlock_owner_waiter AS dow WHERE 1 = 1 AND (dow.database_id = @DatabaseId OR @DatabaseName IS NULL) @@ -30696,7 +30984,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 4, @@ -30711,7 +31000,10 @@ BEGIN nvarchar(20), COUNT_BIG(DISTINCT dp.event_date) ) + - N' instances of Serializable deadlocks.' + N' instances of Serializable deadlocks.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT dp.event_date) DESC) FROM #deadlock_process AS dp WHERE dp.isolation_level LIKE N'serializable%' AND (dp.database_name = @DatabaseName OR @DatabaseName IS NULL) @@ -30736,7 +31028,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 5, @@ -30750,7 +31043,10 @@ BEGIN nvarchar(20), COUNT_BIG(DISTINCT dp.event_date) ) + - N' instances of Repeatable Read deadlocks.' + N' instances of Repeatable Read deadlocks.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT dp.event_date) DESC) FROM #deadlock_process AS dp WHERE dp.isolation_level LIKE N'repeatable%' AND (dp.database_name = @DatabaseName OR @DatabaseName IS NULL) @@ -30775,7 +31071,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 6, @@ -30808,7 +31105,10 @@ BEGIN dp.host_name, N'UNKNOWN' ) + - N'.' + N'.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT dp.event_date) DESC) FROM #deadlock_process AS dp WHERE 1 = 1 AND (dp.database_name = @DatabaseName OR @DatabaseName IS NULL) @@ -30889,7 +31189,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 7, @@ -30916,7 +31217,10 @@ BEGIN 1, 1, N'' - ) + N' locks.' + ) + N' locks.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY CONVERT(bigint, lt.lock_count) DESC) FROM lock_types AS lt OPTION(RECOMPILE); @@ -30930,9 +31234,9 @@ BEGIN deadlock_stack AS ( SELECT DISTINCT - ds.id, - ds.proc_name, - ds.event_date, + ds.id, + ds.event_date, + ds.proc_name, database_name = PARSENAME(ds.proc_name, 3), schema_name = @@ -30966,8 +31270,8 @@ BEGIN PARSENAME(ds.proc_name, 3), PARSENAME(ds.proc_name, 2), PARSENAME(ds.proc_name, 1), - ds.id, ds.proc_name, + ds.id, ds.event_date ) INSERT @@ -30999,6 +31303,7 @@ BEGIN AND (dow.event_date >= @StartDate OR @StartDate IS NULL) AND (dow.event_date < @EndDate OR @EndDate IS NULL) AND (dow.object_name = @StoredProcName OR @StoredProcName IS NULL) + AND ds.proc_name NOT LIKE 'Unknown%' OPTION(RECOMPILE); RAISERROR('Finished at %s', 0, 1, @d) WITH NOWAIT; @@ -31071,7 +31376,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 9, @@ -31090,7 +31396,10 @@ BEGIN nvarchar(10), COUNT_BIG(DISTINCT ds.id) ) + - N' deadlocks.' + N' deadlocks.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT ds.id) DESC) FROM #deadlock_stack AS ds JOIN #deadlock_process AS dp ON dp.id = ds.id @@ -31189,19 +31498,19 @@ BEGIN ) ), wait_time_hms = - /*the more wait time you rack up the less accurate this gets, + /*the more wait time you rack up the less accurate this gets, it's either that or erroring out*/ - CASE - WHEN + CASE + WHEN SUM ( CONVERT ( - bigint, + bigint, dp.wait_time ) )/1000 > 2147483647 - THEN + THEN CONVERT ( nvarchar(30), @@ -31214,7 +31523,7 @@ BEGIN ( CONVERT ( - bigint, + bigint, dp.wait_time ) ) @@ -31225,16 +31534,16 @@ BEGIN ), 14 ) - WHEN + WHEN SUM ( CONVERT ( - bigint, + bigint, dp.wait_time ) ) BETWEEN 2147483648 AND 2147483647000 - THEN + THEN CONVERT ( nvarchar(30), @@ -31247,7 +31556,7 @@ BEGIN ( CONVERT ( - bigint, + bigint, dp.wait_time ) ) @@ -31279,7 +31588,9 @@ BEGIN ), 14 ) - END + END, + total_waits = + SUM(CONVERT(bigint, dp.wait_time)) FROM #deadlock_owner_waiter AS dow JOIN #deadlock_process AS dp ON (dp.id = dow.owner_id @@ -31304,7 +31615,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 11, @@ -31325,7 +31637,10 @@ BEGIN cs.wait_time_hms, 14 ) + - N' [dd hh:mm:ss:ms] of deadlock wait time.' + N' [dd hh:mm:ss:ms] of deadlock wait time.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY cs.total_waits DESC) FROM chopsuey AS cs WHERE cs.object_name IS NOT NULL OPTION(RECOMPILE); @@ -31373,7 +31688,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 12, @@ -31396,19 +31712,19 @@ BEGIN ) ) + N' ' + - /*the more wait time you rack up the less accurate this gets, + /*the more wait time you rack up the less accurate this gets, it's either that or erroring out*/ - CASE - WHEN + CASE + WHEN SUM ( CONVERT ( - bigint, + bigint, wt.total_wait_time_ms ) )/1000 > 2147483647 - THEN + THEN CONVERT ( nvarchar(30), @@ -31421,7 +31737,7 @@ BEGIN ( CONVERT ( - bigint, + bigint, wt.total_wait_time_ms ) ) @@ -31432,16 +31748,16 @@ BEGIN ), 14 ) - WHEN + WHEN SUM ( CONVERT ( - bigint, + bigint, wt.total_wait_time_ms ) ) BETWEEN 2147483648 AND 2147483647000 - THEN + THEN CONVERT ( nvarchar(30), @@ -31454,7 +31770,7 @@ BEGIN ( CONVERT ( - bigint, + bigint, wt.total_wait_time_ms ) ) @@ -31486,7 +31802,10 @@ BEGIN ), 14 ) END + - N' [dd hh:mm:ss:ms] of deadlock wait time.' + N' [dd hh:mm:ss:ms] of deadlock wait time.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY SUM(CONVERT(bigint, wt.total_wait_time_ms)) DESC) FROM wait_time AS wt GROUP BY wt.database_name @@ -31505,7 +31824,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 13, @@ -31520,7 +31840,10 @@ BEGIN finding = N'There have been ' + RTRIM(COUNT_BIG(DISTINCT aj.event_date)) + - N' deadlocks from this Agent Job and Step.' + N' deadlocks from this Agent Job and Step.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT aj.event_date) DESC) FROM #agent_job AS aj GROUP BY DB_NAME(aj.database_id), @@ -32064,7 +32387,8 @@ BEGIN d.waiter_waiting_to_close, /*end parallel deadlock columns*/ d.deadlock_graph, - d.is_victim + d.is_victim, + d.id INTO #deadlock_results FROM #deadlocks AS d; @@ -32267,26 +32591,202 @@ BEGIN DROP SYNONYM DeadlockFindings; /*done with inserting.*/ END; ELSE /*Output to database is not set output to client app*/ - BEGIN - SET @d = CONVERT(varchar(40), GETDATE(), 109); + BEGIN + SET @d = CONVERT(varchar(40), GETDATE(), 109); RAISERROR('Results to client %s', 0, 1, @d) WITH NOWAIT; - + IF @Debug = 1 BEGIN SET STATISTICS XML ON; END; - + EXEC sys.sp_executesql @deadlock_result; - + IF @Debug = 1 BEGIN SET STATISTICS XML OFF; PRINT @deadlock_result; END; - + RAISERROR('Finished at %s', 0, 1, @d) WITH NOWAIT; - + + SET @d = CONVERT(varchar(40), GETDATE(), 109); + RAISERROR('Getting available execution plans for deadlocks %s', 0, 1, @d) WITH NOWAIT; + + SELECT DISTINCT + available_plans = + 'available_plans', + ds.proc_name, + sql_handle = + CONVERT(varbinary(64), ds.sql_handle, 1), + dow.database_name, + dow.database_id, + dow.object_name, + query_xml = + TRY_CAST(dr.query_xml AS nvarchar(MAX)) + INTO #available_plans + FROM #deadlock_stack AS ds + JOIN #deadlock_owner_waiter AS dow + ON dow.owner_id = ds.id + AND dow.event_date = ds.event_date + JOIN #deadlock_results AS dr + ON dr.id = ds.id + AND dr.event_date = ds.event_date + OPTION(RECOMPILE); + + SELECT + deqs.sql_handle, + deqs.plan_handle, + deqs.statement_start_offset, + deqs.statement_end_offset, + deqs.creation_time, + deqs.last_execution_time, + deqs.execution_count, + total_worker_time_ms = + deqs.total_worker_time / 1000., + avg_worker_time_ms = + CONVERT(decimal(38, 6), deqs.total_worker_time / 1000. / deqs.execution_count), + total_elapsed_time_ms = + deqs.total_elapsed_time / 1000., + avg_elapsed_time = + CONVERT(decimal(38, 6), deqs.total_elapsed_time / 1000. / deqs.execution_count), + executions_per_second = + ISNULL + ( + deqs.execution_count / + NULLIF + ( + DATEDIFF + ( + SECOND, + deqs.creation_time, + deqs.last_execution_time + ), + 0 + ), + 0 + ), + total_physical_reads_mb = + deqs.total_physical_reads * 8. / 1024., + total_logical_writes_mb = + deqs.total_logical_writes * 8. / 1024., + total_logical_reads_mb = + deqs.total_logical_reads * 8. / 1024., + min_grant_mb = + deqs.min_grant_kb * 8. / 1024., + max_grant_mb = + deqs.max_grant_kb * 8. / 1024., + min_used_grant_mb = + deqs.min_used_grant_kb * 8. / 1024., + max_used_grant_mb = + deqs.max_used_grant_kb * 8. / 1024., + deqs.min_reserved_threads, + deqs.max_reserved_threads, + deqs.min_used_threads, + deqs.max_used_threads, + deqs.total_rows + INTO #dm_exec_query_stats + FROM sys.dm_exec_query_stats AS deqs + WHERE EXISTS + ( + SELECT + 1/0 + FROM #available_plans AS ap + WHERE ap.sql_handle = deqs.sql_handle + ) + AND deqs.query_hash IS NOT NULL; + + CREATE CLUSTERED INDEX + deqs + ON #dm_exec_query_stats + ( + sql_handle, + plan_handle + ); + + SELECT + ap.available_plans, + ap.database_name, + query_text = + TRY_CAST(ap.query_xml AS xml), + ap.query_plan, + ap.creation_time, + ap.last_execution_time, + ap.execution_count, + ap.executions_per_second, + ap.total_worker_time_ms, + ap.avg_worker_time_ms, + ap.total_elapsed_time_ms, + ap.avg_elapsed_time, + ap.total_logical_reads_mb, + ap.total_physical_reads_mb, + ap.total_logical_writes_mb, + ap.min_grant_mb, + ap.max_grant_mb, + ap.min_used_grant_mb, + ap.max_used_grant_mb, + ap.min_reserved_threads, + ap.max_reserved_threads, + ap.min_used_threads, + ap.max_used_threads, + ap.total_rows, + ap.sql_handle, + ap.statement_start_offset, + ap.statement_end_offset + FROM + ( + + SELECT + ap.*, + c.statement_start_offset, + c.statement_end_offset, + c.creation_time, + c.last_execution_time, + c.execution_count, + c.total_worker_time_ms, + c.avg_worker_time_ms, + c.total_elapsed_time_ms, + c.avg_elapsed_time, + c.executions_per_second, + c.total_physical_reads_mb, + c.total_logical_writes_mb, + c.total_logical_reads_mb, + c.min_grant_mb, + c.max_grant_mb, + c.min_used_grant_mb, + c.max_used_grant_mb, + c.min_reserved_threads, + c.max_reserved_threads, + c.min_used_threads, + c.max_used_threads, + c.total_rows, + c.query_plan + FROM #available_plans AS ap + OUTER APPLY + ( + SELECT + deqs.*, + query_plan = + TRY_CAST(deps.query_plan AS xml) + FROM #dm_exec_query_stats deqs + OUTER APPLY sys.dm_exec_text_query_plan + ( + deqs.plan_handle, + deqs.statement_start_offset, + deqs.statement_end_offset + ) AS deps + WHERE deqs.sql_handle = ap.sql_handle + AND deps.dbid = ap.database_id + ) AS c + ) AS ap + WHERE ap.query_plan IS NOT NULL + ORDER BY + ap.avg_worker_time_ms DESC + OPTION(RECOMPILE, LOOP JOIN, HASH JOIN); + + RAISERROR('Finished at %s', 0, 1, @d) WITH NOWAIT; + SET @d = CONVERT(varchar(40), GETDATE(), 109); RAISERROR('Returning findings %s', 0, 1, @d) WITH NOWAIT; - + SELECT df.check_id, df.database_name, @@ -32294,26 +32794,28 @@ BEGIN df.finding_group, df.finding FROM #deadlock_findings AS df - ORDER BY df.check_id + ORDER BY + df.check_id, + df.sort_order OPTION(RECOMPILE); - + SET @d = CONVERT(varchar(40), GETDATE(), 109); RAISERROR('Finished at %s', 0, 1, @d) WITH NOWAIT; END; /*done with output to client app.*/ - END; + END; IF @Debug = 1 BEGIN SELECT - table_name = N'#dd', + table_name = N'#deadlock_data', * - FROM #dd AS d + FROM #deadlock_data AS dd OPTION(RECOMPILE); SELECT - table_name = N'#deadlock_data', + table_name = N'#dd', * - FROM #deadlock_data AS dd + FROM #dd AS d OPTION(RECOMPILE); SELECT @@ -32370,9 +32872,111 @@ BEGIN FROM @sysAssObjId AS s OPTION(RECOMPILE); + SELECT + table_name = N'#available_plans', + * + FROM #available_plans AS ap + OPTION(RECOMPILE); + + SELECT + table_name = N'#dm_exec_query_stats', + * + FROM #dm_exec_query_stats + OPTION(RECOMPILE); + + SELECT + procedure_parameters = + 'procedure_parameters', + DatabaseName = + @DatabaseName, + StartDate = + @StartDate, + EndDate = + @EndDate, + ObjectName = + @ObjectName, + StoredProcName = + @StoredProcName, + AppName = + @AppName, + HostName = + @HostName, + LoginName = + @LoginName, + EventSessionName = + @EventSessionName, + TargetSessionType = + @TargetSessionType, + VictimsOnly = + @VictimsOnly, + Debug = + @Debug, + Help = + @Help, + Version = + @Version, + VersionDate = + @VersionDate, + VersionCheckMode = + @VersionCheckMode, + OutputDatabaseName = + @OutputDatabaseName, + OutputSchemaName = + @OutputSchemaName, + OutputTableName = + @OutputTableName, + ExportToExcel = + @ExportToExcel; + + SELECT + declared_variables = + 'declared_variables', + DatabaseId = + @DatabaseId, + StartDateUTC = + @StartDateUTC, + EndDateUTC = + @EndDateUTC, + ProductVersion = + @ProductVersion, + ProductVersionMajor = + @ProductVersionMajor, + ProductVersionMinor = + @ProductVersionMinor, + ObjectFullName = + @ObjectFullName, + Azure = + @Azure, + RDS = + @RDS, + d = + @d, + StringToExecute = + @StringToExecute, + StringToExecuteParams = + @StringToExecuteParams, + r = + @r, + OutputTableFindings = + @OutputTableFindings, + DeadlockCount = + @DeadlockCount, + ServerName = + @ServerName, + OutputDatabaseCheck = + @OutputDatabaseCheck, + SessionId = + @SessionId, + TargetSessionId = + @TargetSessionId, + FileName = + @FileName, + inputbuf_bom = + @inputbuf_bom, + deadlock_result = + @deadlock_result; END; /*End debug*/ END; /*Final End*/ - GO SET ANSI_NULLS ON; SET ANSI_PADDING ON; @@ -32433,7 +33037,7 @@ SET NOCOUNT ON; SET STATISTICS XML OFF; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -SELECT @Version = '8.15', @VersionDate = '20230613'; +SELECT @Version = '8.16', @VersionDate = '20230820'; IF(@VersionCheckMode = 1) BEGIN RETURN; @@ -38164,7 +38768,7 @@ BEGIN SET STATISTICS XML OFF; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; - SELECT @Version = '8.15', @VersionDate = '20230613'; + SELECT @Version = '8.16', @VersionDate = '20230820'; IF(@VersionCheckMode = 1) BEGIN @@ -39550,6 +40154,7 @@ ALTER PROCEDURE [dbo].[sp_DatabaseRestore] @DatabaseOwner sysname = NULL, @SetTrustworthyON BIT = 0, @FixOrphanUsers BIT = 0, + @KeepCdc BIT = 0, @Execute CHAR(1) = Y, @FileExtensionDiff NVARCHAR(128) = NULL, @Debug INT = 0, @@ -39563,7 +40168,7 @@ SET STATISTICS XML OFF; /*Versioning details*/ -SELECT @Version = '8.15', @VersionDate = '20230613'; +SELECT @Version = '8.16', @VersionDate = '20230820'; IF(@VersionCheckMode = 1) BEGIN @@ -39884,8 +40489,8 @@ CREATE TABLE #Headers EncryptorThumbprint VARBINARY(20), EncryptorType NVARCHAR(32), LastValidRestoreTime DATETIME, - TimeZone NVARCHAR(256), - CompressionAlgorithm NVARCHAR(256), + TimeZone NVARCHAR(32), + CompressionAlgorithm NVARCHAR(32), -- -- Seq added to retain order by -- @@ -41009,13 +41614,18 @@ END -- Put database in a useable state IF @RunRecovery = 1 BEGIN - SET @sql = N'RESTORE DATABASE ' + @RestoreDatabaseName + N' WITH RECOVERY' + NCHAR(13); + SET @sql = N'RESTORE DATABASE ' + @RestoreDatabaseName + N' WITH RECOVERY'; + + IF @KeepCdc = 1 + SET @sql = @sql + N', KEEP_CDC'; - IF @Debug = 1 OR @Execute = 'N' - BEGIN - IF @sql IS NULL PRINT '@sql is NULL for RESTORE DATABASE: @RestoreDatabaseName'; - PRINT @sql; - END; + SET @sql = @sql + NCHAR(13); + + IF @Debug = 1 OR @Execute = 'N' + BEGIN + IF @sql IS NULL PRINT '@sql is NULL for RESTORE DATABASE: @RestoreDatabaseName'; + PRINT @sql; + END; IF @Debug IN (0, 1) AND @Execute = 'Y' EXECUTE @sql = [dbo].[CommandExecute] @DatabaseContext=N'master', @Command = @sql, @CommandType = 'RECOVER DATABASE', @Mode = 1, @DatabaseName = @UnquotedRestoreDatabaseName, @LogToTable = 'Y', @Execute = 'Y'; @@ -41204,7 +41814,7 @@ BEGIN SET NOCOUNT ON; SET STATISTICS XML OFF; - SELECT @Version = '8.15', @VersionDate = '20230613'; + SELECT @Version = '8.16', @VersionDate = '20230820'; IF(@VersionCheckMode = 1) BEGIN @@ -41550,12 +42160,17 @@ DELETE FROM dbo.SqlServerVersions; INSERT INTO dbo.SqlServerVersions (MajorVersionNumber, MinorVersionNumber, Branch, [Url], ReleaseDate, MainstreamSupportEndDate, ExtendedSupportEndDate, MajorVersionName, MinorVersionName) VALUES + (16, 4065, 'CU7', 'https://support.microsoft.com/en-us/help/5028743', '2023-08-10', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'Cumulative Update 7'), + (16, 4055, 'CU6', 'https://support.microsoft.com/en-us/help/5027505', '2023-07-13', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'Cumulative Update 6'), + (16, 4045, 'CU5', 'https://support.microsoft.com/en-us/help/5026806', '2023-06-15', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'Cumulative Update 5'), (16, 4035, 'CU4', 'https://support.microsoft.com/en-us/help/5026717', '2023-05-11', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'Cumulative Update 4'), (16, 4025, 'CU3', 'https://support.microsoft.com/en-us/help/5024396', '2023-04-13', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'Cumulative Update 3'), (16, 4015, 'CU2', 'https://support.microsoft.com/en-us/help/5023127', '2023-03-15', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'Cumulative Update 2'), (16, 4003, 'CU1', 'https://support.microsoft.com/en-us/help/5022375', '2023-02-16', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'Cumulative Update 1'), (16, 1050, 'RTM GDR', 'https://support.microsoft.com/kb/5021522', '2023-02-14', '2028-01-11', '2033-01-11', 'SQL Server 2022 GDR', 'RTM'), (16, 1000, 'RTM', '', '2022-11-15', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'RTM'), + (15, 4322, 'CU22', 'https://support.microsoft.com/kb/5027702', '2023-08-14', '2025-01-07', '2030-01-08', 'SQL Server 2019', 'Cumulative Update 22'), + (15, 4316, 'CU21', 'https://support.microsoft.com/kb/5025808', '2023-06-15', '2025-01-07', '2030-01-08', 'SQL Server 2019', 'Cumulative Update 21'), (15, 4312, 'CU20', 'https://support.microsoft.com/kb/5024276', '2023-04-13', '2025-01-07', '2030-01-08', 'SQL Server 2019', 'Cumulative Update 20'), (15, 4298, 'CU19', 'https://support.microsoft.com/kb/5023049', '2023-02-16', '2025-01-07', '2030-01-08', 'SQL Server 2019', 'Cumulative Update 19'), (15, 4280, 'CU18 GDR', 'https://support.microsoft.com/kb/5021124', '2023-02-14', '2025-01-07', '2030-01-08', 'SQL Server 2019', 'Cumulative Update 18 GDR'), @@ -41978,7 +42593,7 @@ SET NOCOUNT ON; SET STATISTICS XML OFF; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -SELECT @Version = '8.15', @VersionDate = '20230613'; +SELECT @Version = '8.16', @VersionDate = '20230820'; IF(@VersionCheckMode = 1) BEGIN diff --git a/Install-Core-Blitz-No-Query-Store.sql b/Install-Core-Blitz-No-Query-Store.sql index 7acb71d2..e66bf69a 100644 --- a/Install-Core-Blitz-No-Query-Store.sql +++ b/Install-Core-Blitz-No-Query-Store.sql @@ -38,7 +38,7 @@ AS SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; - SELECT @Version = '8.15', @VersionDate = '20230613'; + SELECT @Version = '8.16', @VersionDate = '20230820'; SET @OutputType = UPPER(@OutputType); IF(@VersionCheckMode = 1) @@ -186,11 +186,129 @@ AS ,@CurrentComponentVersionCheckModeOK BIT ,@canExitLoop BIT ,@frkIsConsistent BIT - ,@NeedToTurnNumericRoundabortBackOn BIT; + ,@NeedToTurnNumericRoundabortBackOn BIT + ,@sa bit = 1 + ,@SUSER_NAME sysname = SUSER_SNAME() + ,@SkipDBCC bit = 0 + ,@SkipTrace bit = 0 + ,@SkipXPRegRead bit = 0 + ,@SkipXPFixedDrives bit = 0 + ,@SkipXPCMDShell bit = 0 + ,@SkipMaster bit = 0 + ,@SkipMSDB bit = 0 + ,@SkipModel bit = 0 + ,@SkipTempDB bit = 0 + ,@SkipValidateLogins bit = 0; + + DECLARE + @db_perms table + ( + database_name sysname, + permission_name sysname + ); + + INSERT + @db_perms + ( + database_name, + permission_name + ) + SELECT + database_name = + DB_NAME(d.database_id), + fmp.permission_name + FROM sys.databases AS d + CROSS APPLY fn_my_permissions(d.name, 'DATABASE') AS fmp + WHERE fmp.permission_name = N'SELECT' /*Databases where we don't have read permissions*/ /* End of declarations for First Responder Kit consistency check:*/ ; + /*Starting permissions checks here, but only if we're not a sysadmin*/ + IF + ( + SELECT + sa = + ISNULL + ( + IS_SRVROLEMEMBER(N'sysadmin'), + 0 + ) + ) = 0 + BEGIN + IF @Debug IN (1, 2) RAISERROR('User not SA, checking permissions', 0, 1) WITH NOWAIT; + + SET @sa = 0; /*Setting this to 0 to skip DBCC COMMANDS*/ + + IF NOT EXISTS + ( + SELECT + 1/0 + FROM sys.fn_my_permissions(NULL, NULL) AS fmp + WHERE fmp.permission_name = N'VIEW SERVER STATE' + ) + BEGIN + RAISERROR('The user %s does not have VIEW SERVER STATE permissions.', 0, 11, @SUSER_NAME) WITH NOWAIT; + RETURN; + END; /*If we don't have this, we can't do anything at all.*/ + + IF NOT EXISTS + ( + SELECT + 1/0 + FROM fn_my_permissions(N'sys.traces', N'OBJECT') AS fmp + WHERE fmp.permission_name = N'ALTER' + ) + BEGIN + SET @SkipTrace = 1; + END; /*We need this permission to execute trace stuff, apparently*/ + + IF NOT EXISTS + ( + SELECT + 1/0 + FROM fn_my_permissions(N'xp_regread', N'OBJECT') AS fmp + WHERE fmp.permission_name = N'EXECUTE' + ) + BEGIN + SET @SkipXPRegRead = 1; + END; /*Need execute on xp_regread*/ + + IF NOT EXISTS + ( + SELECT + 1/0 + FROM fn_my_permissions(N'xp_fixeddrives', N'OBJECT') AS fmp + WHERE fmp.permission_name = N'EXECUTE' + ) + BEGIN + SET @SkipXPFixedDrives = 1; + END; /*Need execute on xp_fixeddrives*/ + + IF NOT EXISTS + ( + SELECT + 1/0 + FROM fn_my_permissions(N'xp_cmdshell', N'OBJECT') AS fmp + WHERE fmp.permission_name = N'EXECUTE' + ) + BEGIN + SET @SkipXPCMDShell = 1; + END; /*Need execute on xp_cmdshell*/ + + IF NOT EXISTS + ( + SELECT + 1/0 + FROM fn_my_permissions(N'sp_validatelogins', N'OBJECT') AS fmp + WHERE fmp.permission_name = N'EXECUTE' + ) + BEGIN + SET @SkipValidateLogins = 1; + END; /*Need execute on sp_validatelogins*/ + + END; + SET @crlf = NCHAR(13) + NCHAR(10); SET @ResultText = 'sp_Blitz Results: ' + @crlf; @@ -331,6 +449,66 @@ AS OR LOWER(d.name) IN ('dbatools', 'dbadmin', 'dbmaintenance')) OPTION(RECOMPILE); + /*Skip checks for database where we don't have read permissions*/ + INSERT INTO + #SkipChecks + ( + DatabaseName + ) + SELECT + DB_NAME(d.database_id) + FROM sys.databases AS d + WHERE NOT EXISTS + ( + SELECT + 1/0 + FROM @db_perms AS dp + WHERE dp.database_name = DB_NAME(d.database_id) + ); + + /*Skip individial checks where we don't have permissions*/ + INSERT #SkipChecks (DatabaseName, CheckID, ServerName) + SELECT + v.* + FROM (VALUES(NULL, 29, NULL)) AS v (DatabaseName, CheckID, ServerName) /*Looks for user tables in model*/ + WHERE NOT EXISTS (SELECT 1/0 FROM @db_perms AS dp WHERE dp.database_name = 'model'); + + INSERT #SkipChecks (DatabaseName, CheckID, ServerName) + SELECT + v.* + FROM (VALUES(NULL, 68, NULL)) AS v (DatabaseName, CheckID, ServerName) /*DBCC command*/ + WHERE @sa = 0; + + INSERT #SkipChecks (DatabaseName, CheckID, ServerName) + SELECT + v.* + FROM (VALUES(NULL, 69, NULL)) AS v (DatabaseName, CheckID, ServerName) /*DBCC command*/ + WHERE @sa = 0; + + INSERT #SkipChecks (DatabaseName, CheckID, ServerName) + SELECT + v.* + FROM (VALUES(NULL, 92, NULL)) AS v (DatabaseName, CheckID, ServerName) /*xp_fixeddrives*/ + WHERE @SkipXPFixedDrives = 1; + + INSERT #SkipChecks (DatabaseName, CheckID, ServerName) + SELECT + v.* + FROM (VALUES(NULL, 211, NULL)) AS v (DatabaseName, CheckID, ServerName) /*xp_regread*/ + WHERE @SkipXPRegRead = 1; + + INSERT #SkipChecks (DatabaseName, CheckID, ServerName) + SELECT + v.* + FROM (VALUES(NULL, 212, NULL)) AS v (DatabaseName, CheckID, ServerName) /*xp_regread*/ + WHERE @SkipXPCMDShell = 1; + + INSERT #SkipChecks (DatabaseName, CheckID, ServerName) + SELECT + v.* + FROM (VALUES(NULL, 2301, NULL)) AS v (DatabaseName, CheckID, ServerName) /*sp_validatelogins*/ + WHERE @SkipValidateLogins = 1 + IF(OBJECT_ID('tempdb..#InvalidLogins') IS NOT NULL) BEGIN EXEC sp_executesql N'DROP TABLE #InvalidLogins;'; @@ -372,7 +550,8 @@ AS SELECT @IsWindowsOperatingSystem = 1 ; END; - IF NOT EXISTS ( SELECT 1 + + IF NOT EXISTS ( SELECT 1 FROM #SkipChecks WHERE DatabaseName IS NULL AND CheckID = 106 ) AND (select convert(int,value_in_use) from sys.configurations where name = 'default trace enabled' ) = 1 @@ -4158,53 +4337,56 @@ AS /* First, let's check that there aren't any issues with the trace files */ BEGIN TRY - - INSERT INTO #fnTraceGettable - ( TextData , - DatabaseName , - EventClass , - Severity , - StartTime , - EndTime , - Duration , - NTUserName , - NTDomainName , - HostName , - ApplicationName , - LoginName , - DBUserName - ) - SELECT TOP 20000 - CONVERT(NVARCHAR(4000),t.TextData) , - t.DatabaseName , - t.EventClass , - t.Severity , - t.StartTime , - t.EndTime , - t.Duration , - t.NTUserName , - t.NTDomainName , - t.HostName , - t.ApplicationName , - t.LoginName , - t.DBUserName - FROM sys.fn_trace_gettable(@base_tracefilename, DEFAULT) t - WHERE - ( - t.EventClass = 22 - AND t.Severity >= 17 - AND t.StartTime > DATEADD(dd, -30, GETDATE()) - ) - OR - ( - t.EventClass IN (92, 93) - AND t.StartTime > DATEADD(dd, -30, GETDATE()) - AND t.Duration > 15000000 - ) - OR - ( - t.EventClass IN (94, 95, 116) - ) + + IF @SkipTrace = 0 + BEGIN + INSERT INTO #fnTraceGettable + ( TextData , + DatabaseName , + EventClass , + Severity , + StartTime , + EndTime , + Duration , + NTUserName , + NTDomainName , + HostName , + ApplicationName , + LoginName , + DBUserName + ) + SELECT TOP 20000 + CONVERT(NVARCHAR(4000),t.TextData) , + t.DatabaseName , + t.EventClass , + t.Severity , + t.StartTime , + t.EndTime , + t.Duration , + t.NTUserName , + t.NTDomainName , + t.HostName , + t.ApplicationName , + t.LoginName , + t.DBUserName + FROM sys.fn_trace_gettable(@base_tracefilename, DEFAULT) t + WHERE + ( + t.EventClass = 22 + AND t.Severity >= 17 + AND t.StartTime > DATEADD(dd, -30, GETDATE()) + ) + OR + ( + t.EventClass IN (92, 93) + AND t.StartTime > DATEADD(dd, -30, GETDATE()) + AND t.Duration > 15000000 + ) + OR + ( + t.EventClass IN (94, 95, 116) + ) + END; SET @TraceFileIssue = 0 @@ -6579,10 +6761,10 @@ IF @ProductVersionMajor >= 10 DatabaseName FROM #SkipChecks WHERE CheckID IS NULL OR CheckID = 19) - AND is_published = 1 + AND (is_published = 1 OR is_subscribed = 1 OR is_merge_published = 1 - OR is_distributor = 1; + OR is_distributor = 1); /* Method B: check subscribers for MSreplication_objects tables */ EXEC dbo.sp_MSforeachdb 'USE [?]; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; @@ -9737,7 +9919,7 @@ AS SET NOCOUNT ON; SET STATISTICS XML OFF; -SELECT @Version = '8.15', @VersionDate = '20230613'; +SELECT @Version = '8.16', @VersionDate = '20230820'; IF(@VersionCheckMode = 1) BEGIN @@ -10615,7 +10797,7 @@ AS SET STATISTICS XML OFF; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; - SELECT @Version = '8.15', @VersionDate = '20230613'; + SELECT @Version = '8.16', @VersionDate = '20230820'; IF(@VersionCheckMode = 1) BEGIN @@ -12397,7 +12579,7 @@ SET NOCOUNT ON; SET STATISTICS XML OFF; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -SELECT @Version = '8.15', @VersionDate = '20230613'; +SELECT @Version = '8.16', @VersionDate = '20230820'; SET @OutputType = UPPER(@OutputType); IF(@VersionCheckMode = 1) @@ -13695,6 +13877,7 @@ BEGIN RAISERROR('Checking for Read intent databases to exclude',0,0) WITH NOWAIT; EXEC('INSERT INTO #ReadableDBs (database_id) SELECT DBs.database_id FROM sys.databases DBs INNER JOIN sys.availability_replicas Replicas ON DBs.replica_id = Replicas.replica_id WHERE replica_server_name NOT IN (SELECT DISTINCT primary_replica FROM sys.dm_hadr_availability_group_states States) AND Replicas.secondary_role_allow_connections_desc = ''READ_ONLY'' AND replica_server_name = @@SERVERNAME OPTION (RECOMPILE);'); + EXEC('INSERT INTO #ReadableDBs VALUES (32767) ;'); -- Exclude internal resource database as well END RAISERROR(N'Checking plan cache age', 0, 1) WITH NOWAIT; @@ -13731,10 +13914,10 @@ WITH total_plans AS SELECT COUNT_BIG(qs.query_plan_hash) AS duplicate_plan_hashes FROM sys.dm_exec_query_stats qs - LEFT JOIN sys.dm_exec_procedure_stats ps - ON qs.sql_handle = ps.sql_handle + LEFT JOIN sys.dm_exec_procedure_stats ps ON qs.plan_handle = ps.plan_handle CROSS APPLY sys.dm_exec_plan_attributes(qs.plan_handle) pa WHERE pa.attribute = N'dbid' + AND pa.value <> 32767 /*Omit Resource database-based queries, we're not going to "fix" them no matter what. Addresses #3314*/ AND qs.query_plan_hash <> 0x0000000000000000 GROUP BY /* qs.query_plan_hash, BGO 20210524 commenting this out to fix #2909 */ @@ -14439,7 +14622,7 @@ BEGIN max_grant_kb AS MaxGrantKB, min_used_grant_kb AS MinUsedGrantKB, max_used_grant_kb AS MaxUsedGrantKB, - CAST(ISNULL(NULLIF(( max_used_grant_kb * 1.00 ), 0) / NULLIF(min_grant_kb, 0), 0) * 100. AS MONEY) AS PercentMemoryGrantUsed, + CAST(ISNULL(NULLIF(( total_used_grant_kb * 1.00 ), 0) / NULLIF(total_grant_kb, 0), 0) * 100. AS MONEY) AS PercentMemoryGrantUsed, CAST(ISNULL(NULLIF(( total_grant_kb * 1. ), 0) / NULLIF(execution_count, 0), 0) AS MONEY) AS AvgMaxMemoryGrant, '; END; ELSE @@ -19715,7 +19898,7 @@ SET NOCOUNT ON; SET STATISTICS XML OFF; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -SELECT @Version = '8.15', @VersionDate = '20230613'; +SELECT @Version = '8.16', @VersionDate = '20230820'; SET @OutputType = UPPER(@OutputType); IF(@VersionCheckMode = 1) @@ -22625,7 +22808,8 @@ BEGIN + CASE WHEN @ShowPartitionRanges = 1 THEN N' COALESCE(range_start_op + '' '' + range_start + '' '', '''') + COALESCE(range_end_op + '' '' + range_end, '''') AS partition_range, ' ELSE N' ' END + N' row_group_id, total_rows, deleted_rows, ' + @ColumnList - + CASE WHEN @ShowPartitionRanges = 1 THEN N' + + CASE WHEN @ShowPartitionRanges = 1 THEN N' , + state_desc, trim_reason_desc, transition_to_compressed_state_desc, has_vertipaq_optimization FROM ( SELECT column_name, partition_number, row_group_id, total_rows, deleted_rows, details, range_start_op, @@ -22635,10 +22819,12 @@ BEGIN range_end_op, CASE WHEN format_type IS NULL THEN CAST(range_end_value AS NVARCHAR(4000)) - ELSE CONVERT(NVARCHAR(4000), range_end_value, format_type) END range_end' ELSE N' ' END + N' + ELSE CONVERT(NVARCHAR(4000), range_end_value, format_type) END range_end' ELSE N' ' END + N', + state_desc, trim_reason_desc, transition_to_compressed_state_desc, has_vertipaq_optimization FROM ( SELECT c.name AS column_name, p.partition_number, rg.row_group_id, rg.total_rows, rg.deleted_rows, - details = CAST(seg.min_data_id AS VARCHAR(20)) + '' to '' + CAST(seg.max_data_id AS VARCHAR(20)) + '', '' + CAST(CAST((seg.on_disk_size / 1024.0 / 1024) AS DECIMAL(18,0)) AS VARCHAR(20)) + '' MB''' + phys.state_desc, phys.trim_reason_desc, phys.transition_to_compressed_state_desc, phys.has_vertipaq_optimization, + details = CAST(seg.min_data_id AS VARCHAR(20)) + '' to '' + CAST(seg.max_data_id AS VARCHAR(20)) + '', '' + CAST(CAST(((COALESCE(d.on_disk_size,0) + COALESCE(seg.on_disk_size,0)) / 1024.0 / 1024) AS DECIMAL(18,0)) AS VARCHAR(20)) + '' MB''' + CASE WHEN @ShowPartitionRanges = 1 THEN N', CASE WHEN pp.system_type_id IN (40, 41, 42, 43, 58, 61) THEN 126 @@ -22652,7 +22838,8 @@ BEGIN FROM ' + QUOTENAME(@DatabaseName) + N'.sys.column_store_row_groups rg INNER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.columns c ON rg.object_id = c.object_id INNER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.partitions p ON rg.object_id = p.object_id AND rg.partition_number = p.partition_number - INNER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.index_columns ic on ic.column_id = c.column_id AND ic.object_id = c.object_id AND ic.index_id = p.index_id ' + CASE WHEN @ShowPartitionRanges = 1 THEN N' + INNER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.index_columns ic on ic.column_id = c.column_id AND ic.object_id = c.object_id AND ic.index_id = p.index_id + LEFT OUTER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.dm_db_column_store_row_group_physical_stats phys ON rg.row_group_id = phys.row_group_id AND rg.object_id = phys.object_id AND rg.partition_number = phys.partition_number AND p.index_id = phys.index_id ' + CASE WHEN @ShowPartitionRanges = 1 THEN N' LEFT OUTER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.indexes i ON i.object_id = rg.object_id AND i.index_id = rg.index_id LEFT OUTER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.partition_schemes ps ON ps.data_space_id = i.data_space_id LEFT OUTER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.partition_functions pf ON pf.function_id = ps.function_id @@ -22660,6 +22847,7 @@ BEGIN LEFT OUTER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.partition_range_values prvs ON prvs.function_id = pf.function_id AND prvs.boundary_id = p.partition_number - 1 LEFT OUTER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.partition_range_values prve ON prve.function_id = pf.function_id AND prve.boundary_id = p.partition_number ' ELSE N' ' END + N' LEFT OUTER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.column_store_segments seg ON p.partition_id = seg.partition_id AND ic.index_column_id = seg.column_id AND rg.row_group_id = seg.segment_id + LEFT OUTER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.column_store_dictionaries d ON p.hobt_id = d.hobt_id AND c.column_id = d.column_id AND seg.secondary_dictionary_id = d.dictionary_id WHERE rg.object_id = @ObjectID AND rg.state IN (1, 2, 3) AND c.name IN ( ' + @ColumnListWithApostrophes + N')' @@ -22697,6 +22885,9 @@ BEGIN RAISERROR(N'Done visualizing columnstore index contents.', 0,1) WITH NOWAIT; END + IF @ShowColumnstoreOnly = 1 + RETURN; + END; /* IF @TableName IS NOT NULL */ @@ -25892,7 +26083,7 @@ BEGIN SET NOCOUNT, XACT_ABORT ON; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; - SELECT @Version = '8.15', @VersionDate = '20230613'; + SELECT @Version = '8.16', @VersionDate = '20230820'; IF @VersionCheckMode = 1 BEGIN @@ -26036,7 +26227,11 @@ BEGIN @TargetSessionId int = 0, @FileName nvarchar(4000) = N'', @inputbuf_bom nvarchar(1) = CONVERT(nvarchar(1), 0x0a00, 0), - @deadlock_result nvarchar(MAX) = N''; + @deadlock_result nvarchar(MAX) = N'', + @StartDateOriginal datetime = @StartDate, + @EndDateOriginal datetime = @EndDate, + @StartDateUTC datetime, + @EndDateUTC datetime; /*Temporary objects used in the procedure*/ DECLARE @@ -26076,15 +26271,17 @@ BEGIN database_name nvarchar(256), object_name nvarchar(1000), finding_group nvarchar(100), - finding nvarchar(4000) + finding nvarchar(4000), + sort_order bigint ); /*Set these to some sane defaults if NULLs are passed in*/ /*Normally I'd hate this, but we RECOMPILE everything*/ + SELECT @StartDate = - CASE - WHEN @StartDate IS NULL + CASE + WHEN @StartDate IS NULL THEN DATEADD ( @@ -26095,18 +26292,25 @@ BEGIN SYSDATETIME(), GETUTCDATE() ), - ISNULL + DATEADD ( - @StartDate, - DATEADD - ( - DAY, - -7, - SYSDATETIME() - ) + DAY, + -7, + SYSDATETIME() ) ) - ELSE @StartDate + ELSE + DATEADD + ( + MINUTE, + DATEDIFF + ( + MINUTE, + SYSDATETIME(), + GETUTCDATE() + ), + @StartDate + ) END, @EndDate = CASE @@ -26121,15 +26325,62 @@ BEGIN SYSDATETIME(), GETUTCDATE() ), - ISNULL + SYSDATETIME() + ) + ELSE + DATEADD + ( + MINUTE, + DATEDIFF ( - @EndDate, - SYSDATETIME() - ) + MINUTE, + SYSDATETIME(), + GETUTCDATE() + ), + @EndDate ) - ELSE @EndDate END; + SELECT + @StartDateUTC = @StartDate, + @EndDateUTC = @EndDate; + + IF @Azure = 0 + BEGIN + IF NOT EXISTS + ( + SELECT + 1/0 + FROM sys.server_event_sessions AS ses + JOIN sys.dm_xe_sessions AS dxs + ON dxs.name = ses.name + WHERE ses.name = @EventSessionName + AND dxs.create_time IS NOT NULL + ) + BEGIN + RAISERROR('A session with the name %s does not exist or is not currently active.', 11, 1, @EventSessionName) WITH NOWAIT; + RETURN; + END; + END; + + IF @Azure = 1 + BEGIN + IF NOT EXISTS + ( + SELECT + 1/0 + FROM sys.database_event_sessions AS ses + JOIN sys.dm_xe_database_sessions AS dxs + ON dxs.name = ses.name + WHERE ses.name = @EventSessionName + AND dxs.create_time IS NOT NULL + ) + BEGIN + RAISERROR('A session with the name %s does not exist or is not currently active.', 11, 1, @EventSessionName) WITH NOWAIT; + RETURN; + END; + END; + IF @OutputDatabaseName IS NOT NULL BEGIN /*IF databaseName is set, do some sanity checks and put [] around def.*/ SET @d = CONVERT(varchar(40), GETDATE(), 109); @@ -26712,8 +26963,7 @@ BEGIN OR e.x.exist('@name[ .= "database_xml_deadlock_report"]') = 1 OR e.x.exist('@name[ .= "xml_deadlock_report_filtered"]') = 1 ) - AND e.x.exist('@timestamp[. >= sql:variable("@StartDate")]') = 1 - AND e.x.exist('@timestamp[. < sql:variable("@EndDate")]') = 1 + AND e.x.exist('@timestamp[. >= sql:variable("@StartDate") and .< sql:variable("@EndDate")]') = 1 OPTION(RECOMPILE); SET @d = CONVERT(varchar(40), GETDATE(), 109); @@ -26730,9 +26980,9 @@ BEGIN SET @d = CONVERT(varchar(40), GETDATE(), 109); RAISERROR('Inserting to #deadlock_data for event file data', 0, 1) WITH NOWAIT; - IF @Debug = 1 BEGIN SET STATISTICS XML ON; END; + IF @Debug = 1 BEGIN SET STATISTICS XML ON; END; - INSERT + INSERT #deadlock_data WITH(TABLOCKX) ( deadlock_xml @@ -26750,13 +27000,12 @@ BEGIN OR e.x.exist('@name[ .= "database_xml_deadlock_report"]') = 1 OR e.x.exist('@name[ .= "xml_deadlock_report_filtered"]') = 1 ) - AND e.x.exist('@timestamp[. >= sql:variable("@StartDate")]') = 1 - AND e.x.exist('@timestamp[. < sql:variable("@EndDate")]') = 1 + AND e.x.exist('@timestamp[. >= sql:variable("@StartDate") and .< sql:variable("@EndDate")]') = 1 OPTION(RECOMPILE); - IF @Debug = 1 BEGIN SET STATISTICS XML OFF; END; + IF @Debug = 1 BEGIN SET STATISTICS XML OFF; END; - SET @d = CONVERT(varchar(40), GETDATE(), 109); + SET @d = CONVERT(varchar(40), GETDATE(), 109); RAISERROR('Finished at %s', 0, 1, @d) WITH NOWAIT; END; @@ -26787,8 +27036,7 @@ BEGIN ) AS xml CROSS APPLY xml.deadlock_xml.nodes('/event') AS e(x) WHERE 1 = 1 - AND e.x.exist('@timestamp[. >= sql:variable("@StartDate")]') = 1 - AND e.x.exist('@timestamp[. < sql:variable("@EndDate")]') = 1 + AND e.x.exist('@timestamp[. >= sql:variable("@StartDate") and .< sql:variable("@EndDate")]') = 1 OPTION(RECOMPILE); INSERT @@ -26915,7 +27163,12 @@ BEGIN DATEADD ( MINUTE, - DATEDIFF(MINUTE, GETUTCDATE(), SYSDATETIME()), + DATEDIFF + ( + MINUTE, + GETUTCDATE(), + SYSDATETIME() + ), dd.event_date ), dd.victim_id, @@ -26973,6 +27226,7 @@ BEGIN FROM #deadlock_process AS dp CROSS APPLY dp.process_xml.nodes('//executionStack/frame') AS ca(dp) WHERE (ca.dp.exist('@procname[. = sql:variable("@StoredProcName")]') = 1 OR @StoredProcName IS NULL) + AND ca.dp.exist('@sqlhandle[ .= "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"]') = 0 OPTION(RECOMPILE); SET @d = CONVERT(varchar(40), GETDATE(), 109); @@ -27045,7 +27299,7 @@ BEGIN waiter_mode = w.l.value('@mode', 'nvarchar(256)'), owner_id = o.l.value('@id', 'nvarchar(256)'), owner_mode = o.l.value('@mode', 'nvarchar(256)'), - lock_type = CAST(N'OBJECT' AS NVARCHAR(100)) + lock_type = CAST(N'OBJECT' AS nvarchar(100)) INTO #deadlock_owner_waiter FROM ( @@ -27441,13 +27695,19 @@ BEGIN 32 ), step_id = - SUBSTRING - ( - dp.client_app, - CHARINDEX(N': Step ', dp.client_app) + LEN(N': Step '), - CHARINDEX(N')', dp.client_app, CHARINDEX(N': Step ', dp.client_app)) - - (CHARINDEX(N': Step ', dp.client_app) + LEN(N': Step ')) - ) + CASE + WHEN CHARINDEX(N': Step ', dp.client_app) > 0 + AND CHARINDEX(N')', dp.client_app, CHARINDEX(N': Step ', dp.client_app)) > 0 + THEN + SUBSTRING + ( + dp.client_app, + CHARINDEX(N': Step ', dp.client_app) + LEN(N': Step '), + CHARINDEX(N')', dp.client_app, CHARINDEX(N': Step ', dp.client_app)) - + (CHARINDEX(N': Step ', dp.client_app) + LEN(N': Step ')) + ) + ELSE dp.client_app + END FROM #deadlock_process AS dp WHERE dp.client_app LIKE N'SQLAgent - %' AND dp.client_app <> N'SQLAgent - Initial Boot Probe' @@ -27597,6 +27857,14 @@ BEGIN /*Begin checks based on parsed values*/ + /* + First, revert these back since we already converted the event data to local time, + and searches will break if we use the times converted over to UTC for the event data + */ + SELECT + @StartDate = @StartDateOriginal, + @EndDate = @EndDateOriginal; + /*Check 1 is deadlocks by database*/ SET @d = CONVERT(varchar(40), GETDATE(), 109); RAISERROR('Check 1 database deadlocks %s', 0, 1, @d) WITH NOWAIT; @@ -27608,7 +27876,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 1, @@ -27622,7 +27891,10 @@ BEGIN nvarchar(20), COUNT_BIG(DISTINCT dp.event_date) ) + - N' deadlocks.' + N' deadlocks.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT dp.event_date) DESC) FROM #deadlock_process AS dp WHERE 1 = 1 AND (dp.database_name = @DatabaseName OR @DatabaseName IS NULL) @@ -27647,7 +27919,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 2, @@ -27662,7 +27935,10 @@ BEGIN nvarchar(20), COUNT_BIG(DISTINCT dow.event_date) ) + - N' deadlock(s) between read queries and modification queries.' + N' deadlock(s) between read queries and modification queries.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT dow.event_date) DESC) FROM #deadlock_owner_waiter AS dow WHERE 1 = 1 AND dow.lock_mode IN @@ -27701,7 +27977,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 3, @@ -27720,7 +27997,10 @@ BEGIN nvarchar(20), COUNT_BIG(DISTINCT dow.event_date) ) + - N' deadlock(s).' + N' deadlock(s).', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT dow.event_date) DESC) FROM #deadlock_owner_waiter AS dow WHERE 1 = 1 AND (dow.database_id = @DatabaseId OR @DatabaseName IS NULL) @@ -27745,7 +28025,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 3, @@ -27759,7 +28040,10 @@ BEGIN nvarchar(20), COUNT_BIG(DISTINCT dow.event_date) ) + - N' deadlock(s).' + N' deadlock(s).', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT dow.event_date) DESC) FROM #deadlock_owner_waiter AS dow WHERE 1 = 1 AND (dow.database_id = @DatabaseId OR @DatabaseName IS NULL) @@ -27790,7 +28074,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 3, @@ -27804,7 +28089,10 @@ BEGIN nvarchar(20), COUNT_BIG(DISTINCT dow.event_date) ) + - N' deadlock(s).' + N' deadlock(s).', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT dow.event_date) DESC) FROM #deadlock_owner_waiter AS dow WHERE 1 = 1 AND (dow.database_id = @DatabaseId OR @DatabaseName IS NULL) @@ -27834,7 +28122,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 4, @@ -27849,7 +28138,10 @@ BEGIN nvarchar(20), COUNT_BIG(DISTINCT dp.event_date) ) + - N' instances of Serializable deadlocks.' + N' instances of Serializable deadlocks.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT dp.event_date) DESC) FROM #deadlock_process AS dp WHERE dp.isolation_level LIKE N'serializable%' AND (dp.database_name = @DatabaseName OR @DatabaseName IS NULL) @@ -27874,7 +28166,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 5, @@ -27888,7 +28181,10 @@ BEGIN nvarchar(20), COUNT_BIG(DISTINCT dp.event_date) ) + - N' instances of Repeatable Read deadlocks.' + N' instances of Repeatable Read deadlocks.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT dp.event_date) DESC) FROM #deadlock_process AS dp WHERE dp.isolation_level LIKE N'repeatable%' AND (dp.database_name = @DatabaseName OR @DatabaseName IS NULL) @@ -27913,7 +28209,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 6, @@ -27946,7 +28243,10 @@ BEGIN dp.host_name, N'UNKNOWN' ) + - N'.' + N'.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT dp.event_date) DESC) FROM #deadlock_process AS dp WHERE 1 = 1 AND (dp.database_name = @DatabaseName OR @DatabaseName IS NULL) @@ -28027,7 +28327,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 7, @@ -28054,7 +28355,10 @@ BEGIN 1, 1, N'' - ) + N' locks.' + ) + N' locks.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY CONVERT(bigint, lt.lock_count) DESC) FROM lock_types AS lt OPTION(RECOMPILE); @@ -28068,9 +28372,9 @@ BEGIN deadlock_stack AS ( SELECT DISTINCT - ds.id, - ds.proc_name, - ds.event_date, + ds.id, + ds.event_date, + ds.proc_name, database_name = PARSENAME(ds.proc_name, 3), schema_name = @@ -28104,8 +28408,8 @@ BEGIN PARSENAME(ds.proc_name, 3), PARSENAME(ds.proc_name, 2), PARSENAME(ds.proc_name, 1), - ds.id, ds.proc_name, + ds.id, ds.event_date ) INSERT @@ -28137,6 +28441,7 @@ BEGIN AND (dow.event_date >= @StartDate OR @StartDate IS NULL) AND (dow.event_date < @EndDate OR @EndDate IS NULL) AND (dow.object_name = @StoredProcName OR @StoredProcName IS NULL) + AND ds.proc_name NOT LIKE 'Unknown%' OPTION(RECOMPILE); RAISERROR('Finished at %s', 0, 1, @d) WITH NOWAIT; @@ -28209,7 +28514,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 9, @@ -28228,7 +28534,10 @@ BEGIN nvarchar(10), COUNT_BIG(DISTINCT ds.id) ) + - N' deadlocks.' + N' deadlocks.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT ds.id) DESC) FROM #deadlock_stack AS ds JOIN #deadlock_process AS dp ON dp.id = ds.id @@ -28327,19 +28636,19 @@ BEGIN ) ), wait_time_hms = - /*the more wait time you rack up the less accurate this gets, + /*the more wait time you rack up the less accurate this gets, it's either that or erroring out*/ - CASE - WHEN + CASE + WHEN SUM ( CONVERT ( - bigint, + bigint, dp.wait_time ) )/1000 > 2147483647 - THEN + THEN CONVERT ( nvarchar(30), @@ -28352,7 +28661,7 @@ BEGIN ( CONVERT ( - bigint, + bigint, dp.wait_time ) ) @@ -28363,16 +28672,16 @@ BEGIN ), 14 ) - WHEN + WHEN SUM ( CONVERT ( - bigint, + bigint, dp.wait_time ) ) BETWEEN 2147483648 AND 2147483647000 - THEN + THEN CONVERT ( nvarchar(30), @@ -28385,7 +28694,7 @@ BEGIN ( CONVERT ( - bigint, + bigint, dp.wait_time ) ) @@ -28417,7 +28726,9 @@ BEGIN ), 14 ) - END + END, + total_waits = + SUM(CONVERT(bigint, dp.wait_time)) FROM #deadlock_owner_waiter AS dow JOIN #deadlock_process AS dp ON (dp.id = dow.owner_id @@ -28442,7 +28753,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 11, @@ -28463,7 +28775,10 @@ BEGIN cs.wait_time_hms, 14 ) + - N' [dd hh:mm:ss:ms] of deadlock wait time.' + N' [dd hh:mm:ss:ms] of deadlock wait time.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY cs.total_waits DESC) FROM chopsuey AS cs WHERE cs.object_name IS NOT NULL OPTION(RECOMPILE); @@ -28511,7 +28826,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 12, @@ -28534,19 +28850,19 @@ BEGIN ) ) + N' ' + - /*the more wait time you rack up the less accurate this gets, + /*the more wait time you rack up the less accurate this gets, it's either that or erroring out*/ - CASE - WHEN + CASE + WHEN SUM ( CONVERT ( - bigint, + bigint, wt.total_wait_time_ms ) )/1000 > 2147483647 - THEN + THEN CONVERT ( nvarchar(30), @@ -28559,7 +28875,7 @@ BEGIN ( CONVERT ( - bigint, + bigint, wt.total_wait_time_ms ) ) @@ -28570,16 +28886,16 @@ BEGIN ), 14 ) - WHEN + WHEN SUM ( CONVERT ( - bigint, + bigint, wt.total_wait_time_ms ) ) BETWEEN 2147483648 AND 2147483647000 - THEN + THEN CONVERT ( nvarchar(30), @@ -28592,7 +28908,7 @@ BEGIN ( CONVERT ( - bigint, + bigint, wt.total_wait_time_ms ) ) @@ -28624,7 +28940,10 @@ BEGIN ), 14 ) END + - N' [dd hh:mm:ss:ms] of deadlock wait time.' + N' [dd hh:mm:ss:ms] of deadlock wait time.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY SUM(CONVERT(bigint, wt.total_wait_time_ms)) DESC) FROM wait_time AS wt GROUP BY wt.database_name @@ -28643,7 +28962,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 13, @@ -28658,7 +28978,10 @@ BEGIN finding = N'There have been ' + RTRIM(COUNT_BIG(DISTINCT aj.event_date)) + - N' deadlocks from this Agent Job and Step.' + N' deadlocks from this Agent Job and Step.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT aj.event_date) DESC) FROM #agent_job AS aj GROUP BY DB_NAME(aj.database_id), @@ -29202,7 +29525,8 @@ BEGIN d.waiter_waiting_to_close, /*end parallel deadlock columns*/ d.deadlock_graph, - d.is_victim + d.is_victim, + d.id INTO #deadlock_results FROM #deadlocks AS d; @@ -29405,26 +29729,202 @@ BEGIN DROP SYNONYM DeadlockFindings; /*done with inserting.*/ END; ELSE /*Output to database is not set output to client app*/ - BEGIN - SET @d = CONVERT(varchar(40), GETDATE(), 109); + BEGIN + SET @d = CONVERT(varchar(40), GETDATE(), 109); RAISERROR('Results to client %s', 0, 1, @d) WITH NOWAIT; - + IF @Debug = 1 BEGIN SET STATISTICS XML ON; END; - + EXEC sys.sp_executesql @deadlock_result; - + IF @Debug = 1 BEGIN SET STATISTICS XML OFF; PRINT @deadlock_result; END; - + RAISERROR('Finished at %s', 0, 1, @d) WITH NOWAIT; - + + SET @d = CONVERT(varchar(40), GETDATE(), 109); + RAISERROR('Getting available execution plans for deadlocks %s', 0, 1, @d) WITH NOWAIT; + + SELECT DISTINCT + available_plans = + 'available_plans', + ds.proc_name, + sql_handle = + CONVERT(varbinary(64), ds.sql_handle, 1), + dow.database_name, + dow.database_id, + dow.object_name, + query_xml = + TRY_CAST(dr.query_xml AS nvarchar(MAX)) + INTO #available_plans + FROM #deadlock_stack AS ds + JOIN #deadlock_owner_waiter AS dow + ON dow.owner_id = ds.id + AND dow.event_date = ds.event_date + JOIN #deadlock_results AS dr + ON dr.id = ds.id + AND dr.event_date = ds.event_date + OPTION(RECOMPILE); + + SELECT + deqs.sql_handle, + deqs.plan_handle, + deqs.statement_start_offset, + deqs.statement_end_offset, + deqs.creation_time, + deqs.last_execution_time, + deqs.execution_count, + total_worker_time_ms = + deqs.total_worker_time / 1000., + avg_worker_time_ms = + CONVERT(decimal(38, 6), deqs.total_worker_time / 1000. / deqs.execution_count), + total_elapsed_time_ms = + deqs.total_elapsed_time / 1000., + avg_elapsed_time = + CONVERT(decimal(38, 6), deqs.total_elapsed_time / 1000. / deqs.execution_count), + executions_per_second = + ISNULL + ( + deqs.execution_count / + NULLIF + ( + DATEDIFF + ( + SECOND, + deqs.creation_time, + deqs.last_execution_time + ), + 0 + ), + 0 + ), + total_physical_reads_mb = + deqs.total_physical_reads * 8. / 1024., + total_logical_writes_mb = + deqs.total_logical_writes * 8. / 1024., + total_logical_reads_mb = + deqs.total_logical_reads * 8. / 1024., + min_grant_mb = + deqs.min_grant_kb * 8. / 1024., + max_grant_mb = + deqs.max_grant_kb * 8. / 1024., + min_used_grant_mb = + deqs.min_used_grant_kb * 8. / 1024., + max_used_grant_mb = + deqs.max_used_grant_kb * 8. / 1024., + deqs.min_reserved_threads, + deqs.max_reserved_threads, + deqs.min_used_threads, + deqs.max_used_threads, + deqs.total_rows + INTO #dm_exec_query_stats + FROM sys.dm_exec_query_stats AS deqs + WHERE EXISTS + ( + SELECT + 1/0 + FROM #available_plans AS ap + WHERE ap.sql_handle = deqs.sql_handle + ) + AND deqs.query_hash IS NOT NULL; + + CREATE CLUSTERED INDEX + deqs + ON #dm_exec_query_stats + ( + sql_handle, + plan_handle + ); + + SELECT + ap.available_plans, + ap.database_name, + query_text = + TRY_CAST(ap.query_xml AS xml), + ap.query_plan, + ap.creation_time, + ap.last_execution_time, + ap.execution_count, + ap.executions_per_second, + ap.total_worker_time_ms, + ap.avg_worker_time_ms, + ap.total_elapsed_time_ms, + ap.avg_elapsed_time, + ap.total_logical_reads_mb, + ap.total_physical_reads_mb, + ap.total_logical_writes_mb, + ap.min_grant_mb, + ap.max_grant_mb, + ap.min_used_grant_mb, + ap.max_used_grant_mb, + ap.min_reserved_threads, + ap.max_reserved_threads, + ap.min_used_threads, + ap.max_used_threads, + ap.total_rows, + ap.sql_handle, + ap.statement_start_offset, + ap.statement_end_offset + FROM + ( + + SELECT + ap.*, + c.statement_start_offset, + c.statement_end_offset, + c.creation_time, + c.last_execution_time, + c.execution_count, + c.total_worker_time_ms, + c.avg_worker_time_ms, + c.total_elapsed_time_ms, + c.avg_elapsed_time, + c.executions_per_second, + c.total_physical_reads_mb, + c.total_logical_writes_mb, + c.total_logical_reads_mb, + c.min_grant_mb, + c.max_grant_mb, + c.min_used_grant_mb, + c.max_used_grant_mb, + c.min_reserved_threads, + c.max_reserved_threads, + c.min_used_threads, + c.max_used_threads, + c.total_rows, + c.query_plan + FROM #available_plans AS ap + OUTER APPLY + ( + SELECT + deqs.*, + query_plan = + TRY_CAST(deps.query_plan AS xml) + FROM #dm_exec_query_stats deqs + OUTER APPLY sys.dm_exec_text_query_plan + ( + deqs.plan_handle, + deqs.statement_start_offset, + deqs.statement_end_offset + ) AS deps + WHERE deqs.sql_handle = ap.sql_handle + AND deps.dbid = ap.database_id + ) AS c + ) AS ap + WHERE ap.query_plan IS NOT NULL + ORDER BY + ap.avg_worker_time_ms DESC + OPTION(RECOMPILE, LOOP JOIN, HASH JOIN); + + RAISERROR('Finished at %s', 0, 1, @d) WITH NOWAIT; + SET @d = CONVERT(varchar(40), GETDATE(), 109); RAISERROR('Returning findings %s', 0, 1, @d) WITH NOWAIT; - + SELECT df.check_id, df.database_name, @@ -29432,26 +29932,28 @@ BEGIN df.finding_group, df.finding FROM #deadlock_findings AS df - ORDER BY df.check_id + ORDER BY + df.check_id, + df.sort_order OPTION(RECOMPILE); - + SET @d = CONVERT(varchar(40), GETDATE(), 109); RAISERROR('Finished at %s', 0, 1, @d) WITH NOWAIT; END; /*done with output to client app.*/ - END; + END; IF @Debug = 1 BEGIN SELECT - table_name = N'#dd', + table_name = N'#deadlock_data', * - FROM #dd AS d + FROM #deadlock_data AS dd OPTION(RECOMPILE); SELECT - table_name = N'#deadlock_data', + table_name = N'#dd', * - FROM #deadlock_data AS dd + FROM #dd AS d OPTION(RECOMPILE); SELECT @@ -29508,9 +30010,111 @@ BEGIN FROM @sysAssObjId AS s OPTION(RECOMPILE); + SELECT + table_name = N'#available_plans', + * + FROM #available_plans AS ap + OPTION(RECOMPILE); + + SELECT + table_name = N'#dm_exec_query_stats', + * + FROM #dm_exec_query_stats + OPTION(RECOMPILE); + + SELECT + procedure_parameters = + 'procedure_parameters', + DatabaseName = + @DatabaseName, + StartDate = + @StartDate, + EndDate = + @EndDate, + ObjectName = + @ObjectName, + StoredProcName = + @StoredProcName, + AppName = + @AppName, + HostName = + @HostName, + LoginName = + @LoginName, + EventSessionName = + @EventSessionName, + TargetSessionType = + @TargetSessionType, + VictimsOnly = + @VictimsOnly, + Debug = + @Debug, + Help = + @Help, + Version = + @Version, + VersionDate = + @VersionDate, + VersionCheckMode = + @VersionCheckMode, + OutputDatabaseName = + @OutputDatabaseName, + OutputSchemaName = + @OutputSchemaName, + OutputTableName = + @OutputTableName, + ExportToExcel = + @ExportToExcel; + + SELECT + declared_variables = + 'declared_variables', + DatabaseId = + @DatabaseId, + StartDateUTC = + @StartDateUTC, + EndDateUTC = + @EndDateUTC, + ProductVersion = + @ProductVersion, + ProductVersionMajor = + @ProductVersionMajor, + ProductVersionMinor = + @ProductVersionMinor, + ObjectFullName = + @ObjectFullName, + Azure = + @Azure, + RDS = + @RDS, + d = + @d, + StringToExecute = + @StringToExecute, + StringToExecuteParams = + @StringToExecuteParams, + r = + @r, + OutputTableFindings = + @OutputTableFindings, + DeadlockCount = + @DeadlockCount, + ServerName = + @ServerName, + OutputDatabaseCheck = + @OutputDatabaseCheck, + SessionId = + @SessionId, + TargetSessionId = + @TargetSessionId, + FileName = + @FileName, + inputbuf_bom = + @inputbuf_bom, + deadlock_result = + @deadlock_result; END; /*End debug*/ END; /*Final End*/ - GO IF OBJECT_ID('dbo.sp_BlitzWho') IS NULL EXEC ('CREATE PROCEDURE dbo.sp_BlitzWho AS RETURN 0;') @@ -29547,7 +30151,7 @@ BEGIN SET STATISTICS XML OFF; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; - SELECT @Version = '8.15', @VersionDate = '20230613'; + SELECT @Version = '8.16', @VersionDate = '20230820'; IF(@VersionCheckMode = 1) BEGIN @@ -30943,12 +31547,17 @@ DELETE FROM dbo.SqlServerVersions; INSERT INTO dbo.SqlServerVersions (MajorVersionNumber, MinorVersionNumber, Branch, [Url], ReleaseDate, MainstreamSupportEndDate, ExtendedSupportEndDate, MajorVersionName, MinorVersionName) VALUES + (16, 4065, 'CU7', 'https://support.microsoft.com/en-us/help/5028743', '2023-08-10', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'Cumulative Update 7'), + (16, 4055, 'CU6', 'https://support.microsoft.com/en-us/help/5027505', '2023-07-13', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'Cumulative Update 6'), + (16, 4045, 'CU5', 'https://support.microsoft.com/en-us/help/5026806', '2023-06-15', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'Cumulative Update 5'), (16, 4035, 'CU4', 'https://support.microsoft.com/en-us/help/5026717', '2023-05-11', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'Cumulative Update 4'), (16, 4025, 'CU3', 'https://support.microsoft.com/en-us/help/5024396', '2023-04-13', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'Cumulative Update 3'), (16, 4015, 'CU2', 'https://support.microsoft.com/en-us/help/5023127', '2023-03-15', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'Cumulative Update 2'), (16, 4003, 'CU1', 'https://support.microsoft.com/en-us/help/5022375', '2023-02-16', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'Cumulative Update 1'), (16, 1050, 'RTM GDR', 'https://support.microsoft.com/kb/5021522', '2023-02-14', '2028-01-11', '2033-01-11', 'SQL Server 2022 GDR', 'RTM'), (16, 1000, 'RTM', '', '2022-11-15', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'RTM'), + (15, 4322, 'CU22', 'https://support.microsoft.com/kb/5027702', '2023-08-14', '2025-01-07', '2030-01-08', 'SQL Server 2019', 'Cumulative Update 22'), + (15, 4316, 'CU21', 'https://support.microsoft.com/kb/5025808', '2023-06-15', '2025-01-07', '2030-01-08', 'SQL Server 2019', 'Cumulative Update 21'), (15, 4312, 'CU20', 'https://support.microsoft.com/kb/5024276', '2023-04-13', '2025-01-07', '2030-01-08', 'SQL Server 2019', 'Cumulative Update 20'), (15, 4298, 'CU19', 'https://support.microsoft.com/kb/5023049', '2023-02-16', '2025-01-07', '2030-01-08', 'SQL Server 2019', 'Cumulative Update 19'), (15, 4280, 'CU18 GDR', 'https://support.microsoft.com/kb/5021124', '2023-02-14', '2025-01-07', '2030-01-08', 'SQL Server 2019', 'Cumulative Update 18 GDR'), @@ -31371,7 +31980,7 @@ SET NOCOUNT ON; SET STATISTICS XML OFF; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -SELECT @Version = '8.15', @VersionDate = '20230613'; +SELECT @Version = '8.16', @VersionDate = '20230820'; IF(@VersionCheckMode = 1) BEGIN diff --git a/Install-Core-Blitz-With-Query-Store.sql b/Install-Core-Blitz-With-Query-Store.sql index b06e0754..bc39b6f6 100644 --- a/Install-Core-Blitz-With-Query-Store.sql +++ b/Install-Core-Blitz-With-Query-Store.sql @@ -38,7 +38,7 @@ AS SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; - SELECT @Version = '8.15', @VersionDate = '20230613'; + SELECT @Version = '8.16', @VersionDate = '20230820'; SET @OutputType = UPPER(@OutputType); IF(@VersionCheckMode = 1) @@ -186,11 +186,129 @@ AS ,@CurrentComponentVersionCheckModeOK BIT ,@canExitLoop BIT ,@frkIsConsistent BIT - ,@NeedToTurnNumericRoundabortBackOn BIT; + ,@NeedToTurnNumericRoundabortBackOn BIT + ,@sa bit = 1 + ,@SUSER_NAME sysname = SUSER_SNAME() + ,@SkipDBCC bit = 0 + ,@SkipTrace bit = 0 + ,@SkipXPRegRead bit = 0 + ,@SkipXPFixedDrives bit = 0 + ,@SkipXPCMDShell bit = 0 + ,@SkipMaster bit = 0 + ,@SkipMSDB bit = 0 + ,@SkipModel bit = 0 + ,@SkipTempDB bit = 0 + ,@SkipValidateLogins bit = 0; + + DECLARE + @db_perms table + ( + database_name sysname, + permission_name sysname + ); + + INSERT + @db_perms + ( + database_name, + permission_name + ) + SELECT + database_name = + DB_NAME(d.database_id), + fmp.permission_name + FROM sys.databases AS d + CROSS APPLY fn_my_permissions(d.name, 'DATABASE') AS fmp + WHERE fmp.permission_name = N'SELECT' /*Databases where we don't have read permissions*/ /* End of declarations for First Responder Kit consistency check:*/ ; + /*Starting permissions checks here, but only if we're not a sysadmin*/ + IF + ( + SELECT + sa = + ISNULL + ( + IS_SRVROLEMEMBER(N'sysadmin'), + 0 + ) + ) = 0 + BEGIN + IF @Debug IN (1, 2) RAISERROR('User not SA, checking permissions', 0, 1) WITH NOWAIT; + + SET @sa = 0; /*Setting this to 0 to skip DBCC COMMANDS*/ + + IF NOT EXISTS + ( + SELECT + 1/0 + FROM sys.fn_my_permissions(NULL, NULL) AS fmp + WHERE fmp.permission_name = N'VIEW SERVER STATE' + ) + BEGIN + RAISERROR('The user %s does not have VIEW SERVER STATE permissions.', 0, 11, @SUSER_NAME) WITH NOWAIT; + RETURN; + END; /*If we don't have this, we can't do anything at all.*/ + + IF NOT EXISTS + ( + SELECT + 1/0 + FROM fn_my_permissions(N'sys.traces', N'OBJECT') AS fmp + WHERE fmp.permission_name = N'ALTER' + ) + BEGIN + SET @SkipTrace = 1; + END; /*We need this permission to execute trace stuff, apparently*/ + + IF NOT EXISTS + ( + SELECT + 1/0 + FROM fn_my_permissions(N'xp_regread', N'OBJECT') AS fmp + WHERE fmp.permission_name = N'EXECUTE' + ) + BEGIN + SET @SkipXPRegRead = 1; + END; /*Need execute on xp_regread*/ + + IF NOT EXISTS + ( + SELECT + 1/0 + FROM fn_my_permissions(N'xp_fixeddrives', N'OBJECT') AS fmp + WHERE fmp.permission_name = N'EXECUTE' + ) + BEGIN + SET @SkipXPFixedDrives = 1; + END; /*Need execute on xp_fixeddrives*/ + + IF NOT EXISTS + ( + SELECT + 1/0 + FROM fn_my_permissions(N'xp_cmdshell', N'OBJECT') AS fmp + WHERE fmp.permission_name = N'EXECUTE' + ) + BEGIN + SET @SkipXPCMDShell = 1; + END; /*Need execute on xp_cmdshell*/ + + IF NOT EXISTS + ( + SELECT + 1/0 + FROM fn_my_permissions(N'sp_validatelogins', N'OBJECT') AS fmp + WHERE fmp.permission_name = N'EXECUTE' + ) + BEGIN + SET @SkipValidateLogins = 1; + END; /*Need execute on sp_validatelogins*/ + + END; + SET @crlf = NCHAR(13) + NCHAR(10); SET @ResultText = 'sp_Blitz Results: ' + @crlf; @@ -331,6 +449,66 @@ AS OR LOWER(d.name) IN ('dbatools', 'dbadmin', 'dbmaintenance')) OPTION(RECOMPILE); + /*Skip checks for database where we don't have read permissions*/ + INSERT INTO + #SkipChecks + ( + DatabaseName + ) + SELECT + DB_NAME(d.database_id) + FROM sys.databases AS d + WHERE NOT EXISTS + ( + SELECT + 1/0 + FROM @db_perms AS dp + WHERE dp.database_name = DB_NAME(d.database_id) + ); + + /*Skip individial checks where we don't have permissions*/ + INSERT #SkipChecks (DatabaseName, CheckID, ServerName) + SELECT + v.* + FROM (VALUES(NULL, 29, NULL)) AS v (DatabaseName, CheckID, ServerName) /*Looks for user tables in model*/ + WHERE NOT EXISTS (SELECT 1/0 FROM @db_perms AS dp WHERE dp.database_name = 'model'); + + INSERT #SkipChecks (DatabaseName, CheckID, ServerName) + SELECT + v.* + FROM (VALUES(NULL, 68, NULL)) AS v (DatabaseName, CheckID, ServerName) /*DBCC command*/ + WHERE @sa = 0; + + INSERT #SkipChecks (DatabaseName, CheckID, ServerName) + SELECT + v.* + FROM (VALUES(NULL, 69, NULL)) AS v (DatabaseName, CheckID, ServerName) /*DBCC command*/ + WHERE @sa = 0; + + INSERT #SkipChecks (DatabaseName, CheckID, ServerName) + SELECT + v.* + FROM (VALUES(NULL, 92, NULL)) AS v (DatabaseName, CheckID, ServerName) /*xp_fixeddrives*/ + WHERE @SkipXPFixedDrives = 1; + + INSERT #SkipChecks (DatabaseName, CheckID, ServerName) + SELECT + v.* + FROM (VALUES(NULL, 211, NULL)) AS v (DatabaseName, CheckID, ServerName) /*xp_regread*/ + WHERE @SkipXPRegRead = 1; + + INSERT #SkipChecks (DatabaseName, CheckID, ServerName) + SELECT + v.* + FROM (VALUES(NULL, 212, NULL)) AS v (DatabaseName, CheckID, ServerName) /*xp_regread*/ + WHERE @SkipXPCMDShell = 1; + + INSERT #SkipChecks (DatabaseName, CheckID, ServerName) + SELECT + v.* + FROM (VALUES(NULL, 2301, NULL)) AS v (DatabaseName, CheckID, ServerName) /*sp_validatelogins*/ + WHERE @SkipValidateLogins = 1 + IF(OBJECT_ID('tempdb..#InvalidLogins') IS NOT NULL) BEGIN EXEC sp_executesql N'DROP TABLE #InvalidLogins;'; @@ -372,7 +550,8 @@ AS SELECT @IsWindowsOperatingSystem = 1 ; END; - IF NOT EXISTS ( SELECT 1 + + IF NOT EXISTS ( SELECT 1 FROM #SkipChecks WHERE DatabaseName IS NULL AND CheckID = 106 ) AND (select convert(int,value_in_use) from sys.configurations where name = 'default trace enabled' ) = 1 @@ -4158,53 +4337,56 @@ AS /* First, let's check that there aren't any issues with the trace files */ BEGIN TRY - - INSERT INTO #fnTraceGettable - ( TextData , - DatabaseName , - EventClass , - Severity , - StartTime , - EndTime , - Duration , - NTUserName , - NTDomainName , - HostName , - ApplicationName , - LoginName , - DBUserName - ) - SELECT TOP 20000 - CONVERT(NVARCHAR(4000),t.TextData) , - t.DatabaseName , - t.EventClass , - t.Severity , - t.StartTime , - t.EndTime , - t.Duration , - t.NTUserName , - t.NTDomainName , - t.HostName , - t.ApplicationName , - t.LoginName , - t.DBUserName - FROM sys.fn_trace_gettable(@base_tracefilename, DEFAULT) t - WHERE - ( - t.EventClass = 22 - AND t.Severity >= 17 - AND t.StartTime > DATEADD(dd, -30, GETDATE()) - ) - OR - ( - t.EventClass IN (92, 93) - AND t.StartTime > DATEADD(dd, -30, GETDATE()) - AND t.Duration > 15000000 - ) - OR - ( - t.EventClass IN (94, 95, 116) - ) + + IF @SkipTrace = 0 + BEGIN + INSERT INTO #fnTraceGettable + ( TextData , + DatabaseName , + EventClass , + Severity , + StartTime , + EndTime , + Duration , + NTUserName , + NTDomainName , + HostName , + ApplicationName , + LoginName , + DBUserName + ) + SELECT TOP 20000 + CONVERT(NVARCHAR(4000),t.TextData) , + t.DatabaseName , + t.EventClass , + t.Severity , + t.StartTime , + t.EndTime , + t.Duration , + t.NTUserName , + t.NTDomainName , + t.HostName , + t.ApplicationName , + t.LoginName , + t.DBUserName + FROM sys.fn_trace_gettable(@base_tracefilename, DEFAULT) t + WHERE + ( + t.EventClass = 22 + AND t.Severity >= 17 + AND t.StartTime > DATEADD(dd, -30, GETDATE()) + ) + OR + ( + t.EventClass IN (92, 93) + AND t.StartTime > DATEADD(dd, -30, GETDATE()) + AND t.Duration > 15000000 + ) + OR + ( + t.EventClass IN (94, 95, 116) + ) + END; SET @TraceFileIssue = 0 @@ -6579,10 +6761,10 @@ IF @ProductVersionMajor >= 10 DatabaseName FROM #SkipChecks WHERE CheckID IS NULL OR CheckID = 19) - AND is_published = 1 + AND (is_published = 1 OR is_subscribed = 1 OR is_merge_published = 1 - OR is_distributor = 1; + OR is_distributor = 1); /* Method B: check subscribers for MSreplication_objects tables */ EXEC dbo.sp_MSforeachdb 'USE [?]; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; @@ -9737,7 +9919,7 @@ AS SET NOCOUNT ON; SET STATISTICS XML OFF; -SELECT @Version = '8.15', @VersionDate = '20230613'; +SELECT @Version = '8.16', @VersionDate = '20230820'; IF(@VersionCheckMode = 1) BEGIN @@ -10615,7 +10797,7 @@ AS SET STATISTICS XML OFF; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; - SELECT @Version = '8.15', @VersionDate = '20230613'; + SELECT @Version = '8.16', @VersionDate = '20230820'; IF(@VersionCheckMode = 1) BEGIN @@ -12397,7 +12579,7 @@ SET NOCOUNT ON; SET STATISTICS XML OFF; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -SELECT @Version = '8.15', @VersionDate = '20230613'; +SELECT @Version = '8.16', @VersionDate = '20230820'; SET @OutputType = UPPER(@OutputType); IF(@VersionCheckMode = 1) @@ -13695,6 +13877,7 @@ BEGIN RAISERROR('Checking for Read intent databases to exclude',0,0) WITH NOWAIT; EXEC('INSERT INTO #ReadableDBs (database_id) SELECT DBs.database_id FROM sys.databases DBs INNER JOIN sys.availability_replicas Replicas ON DBs.replica_id = Replicas.replica_id WHERE replica_server_name NOT IN (SELECT DISTINCT primary_replica FROM sys.dm_hadr_availability_group_states States) AND Replicas.secondary_role_allow_connections_desc = ''READ_ONLY'' AND replica_server_name = @@SERVERNAME OPTION (RECOMPILE);'); + EXEC('INSERT INTO #ReadableDBs VALUES (32767) ;'); -- Exclude internal resource database as well END RAISERROR(N'Checking plan cache age', 0, 1) WITH NOWAIT; @@ -13731,10 +13914,10 @@ WITH total_plans AS SELECT COUNT_BIG(qs.query_plan_hash) AS duplicate_plan_hashes FROM sys.dm_exec_query_stats qs - LEFT JOIN sys.dm_exec_procedure_stats ps - ON qs.sql_handle = ps.sql_handle + LEFT JOIN sys.dm_exec_procedure_stats ps ON qs.plan_handle = ps.plan_handle CROSS APPLY sys.dm_exec_plan_attributes(qs.plan_handle) pa WHERE pa.attribute = N'dbid' + AND pa.value <> 32767 /*Omit Resource database-based queries, we're not going to "fix" them no matter what. Addresses #3314*/ AND qs.query_plan_hash <> 0x0000000000000000 GROUP BY /* qs.query_plan_hash, BGO 20210524 commenting this out to fix #2909 */ @@ -14439,7 +14622,7 @@ BEGIN max_grant_kb AS MaxGrantKB, min_used_grant_kb AS MinUsedGrantKB, max_used_grant_kb AS MaxUsedGrantKB, - CAST(ISNULL(NULLIF(( max_used_grant_kb * 1.00 ), 0) / NULLIF(min_grant_kb, 0), 0) * 100. AS MONEY) AS PercentMemoryGrantUsed, + CAST(ISNULL(NULLIF(( total_used_grant_kb * 1.00 ), 0) / NULLIF(total_grant_kb, 0), 0) * 100. AS MONEY) AS PercentMemoryGrantUsed, CAST(ISNULL(NULLIF(( total_grant_kb * 1. ), 0) / NULLIF(execution_count, 0), 0) AS MONEY) AS AvgMaxMemoryGrant, '; END; ELSE @@ -19715,7 +19898,7 @@ SET NOCOUNT ON; SET STATISTICS XML OFF; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -SELECT @Version = '8.15', @VersionDate = '20230613'; +SELECT @Version = '8.16', @VersionDate = '20230820'; SET @OutputType = UPPER(@OutputType); IF(@VersionCheckMode = 1) @@ -22625,7 +22808,8 @@ BEGIN + CASE WHEN @ShowPartitionRanges = 1 THEN N' COALESCE(range_start_op + '' '' + range_start + '' '', '''') + COALESCE(range_end_op + '' '' + range_end, '''') AS partition_range, ' ELSE N' ' END + N' row_group_id, total_rows, deleted_rows, ' + @ColumnList - + CASE WHEN @ShowPartitionRanges = 1 THEN N' + + CASE WHEN @ShowPartitionRanges = 1 THEN N' , + state_desc, trim_reason_desc, transition_to_compressed_state_desc, has_vertipaq_optimization FROM ( SELECT column_name, partition_number, row_group_id, total_rows, deleted_rows, details, range_start_op, @@ -22635,10 +22819,12 @@ BEGIN range_end_op, CASE WHEN format_type IS NULL THEN CAST(range_end_value AS NVARCHAR(4000)) - ELSE CONVERT(NVARCHAR(4000), range_end_value, format_type) END range_end' ELSE N' ' END + N' + ELSE CONVERT(NVARCHAR(4000), range_end_value, format_type) END range_end' ELSE N' ' END + N', + state_desc, trim_reason_desc, transition_to_compressed_state_desc, has_vertipaq_optimization FROM ( SELECT c.name AS column_name, p.partition_number, rg.row_group_id, rg.total_rows, rg.deleted_rows, - details = CAST(seg.min_data_id AS VARCHAR(20)) + '' to '' + CAST(seg.max_data_id AS VARCHAR(20)) + '', '' + CAST(CAST((seg.on_disk_size / 1024.0 / 1024) AS DECIMAL(18,0)) AS VARCHAR(20)) + '' MB''' + phys.state_desc, phys.trim_reason_desc, phys.transition_to_compressed_state_desc, phys.has_vertipaq_optimization, + details = CAST(seg.min_data_id AS VARCHAR(20)) + '' to '' + CAST(seg.max_data_id AS VARCHAR(20)) + '', '' + CAST(CAST(((COALESCE(d.on_disk_size,0) + COALESCE(seg.on_disk_size,0)) / 1024.0 / 1024) AS DECIMAL(18,0)) AS VARCHAR(20)) + '' MB''' + CASE WHEN @ShowPartitionRanges = 1 THEN N', CASE WHEN pp.system_type_id IN (40, 41, 42, 43, 58, 61) THEN 126 @@ -22652,7 +22838,8 @@ BEGIN FROM ' + QUOTENAME(@DatabaseName) + N'.sys.column_store_row_groups rg INNER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.columns c ON rg.object_id = c.object_id INNER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.partitions p ON rg.object_id = p.object_id AND rg.partition_number = p.partition_number - INNER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.index_columns ic on ic.column_id = c.column_id AND ic.object_id = c.object_id AND ic.index_id = p.index_id ' + CASE WHEN @ShowPartitionRanges = 1 THEN N' + INNER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.index_columns ic on ic.column_id = c.column_id AND ic.object_id = c.object_id AND ic.index_id = p.index_id + LEFT OUTER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.dm_db_column_store_row_group_physical_stats phys ON rg.row_group_id = phys.row_group_id AND rg.object_id = phys.object_id AND rg.partition_number = phys.partition_number AND p.index_id = phys.index_id ' + CASE WHEN @ShowPartitionRanges = 1 THEN N' LEFT OUTER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.indexes i ON i.object_id = rg.object_id AND i.index_id = rg.index_id LEFT OUTER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.partition_schemes ps ON ps.data_space_id = i.data_space_id LEFT OUTER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.partition_functions pf ON pf.function_id = ps.function_id @@ -22660,6 +22847,7 @@ BEGIN LEFT OUTER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.partition_range_values prvs ON prvs.function_id = pf.function_id AND prvs.boundary_id = p.partition_number - 1 LEFT OUTER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.partition_range_values prve ON prve.function_id = pf.function_id AND prve.boundary_id = p.partition_number ' ELSE N' ' END + N' LEFT OUTER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.column_store_segments seg ON p.partition_id = seg.partition_id AND ic.index_column_id = seg.column_id AND rg.row_group_id = seg.segment_id + LEFT OUTER JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.column_store_dictionaries d ON p.hobt_id = d.hobt_id AND c.column_id = d.column_id AND seg.secondary_dictionary_id = d.dictionary_id WHERE rg.object_id = @ObjectID AND rg.state IN (1, 2, 3) AND c.name IN ( ' + @ColumnListWithApostrophes + N')' @@ -22697,6 +22885,9 @@ BEGIN RAISERROR(N'Done visualizing columnstore index contents.', 0,1) WITH NOWAIT; END + IF @ShowColumnstoreOnly = 1 + RETURN; + END; /* IF @TableName IS NOT NULL */ @@ -25892,7 +26083,7 @@ BEGIN SET NOCOUNT, XACT_ABORT ON; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; - SELECT @Version = '8.15', @VersionDate = '20230613'; + SELECT @Version = '8.16', @VersionDate = '20230820'; IF @VersionCheckMode = 1 BEGIN @@ -26036,7 +26227,11 @@ BEGIN @TargetSessionId int = 0, @FileName nvarchar(4000) = N'', @inputbuf_bom nvarchar(1) = CONVERT(nvarchar(1), 0x0a00, 0), - @deadlock_result nvarchar(MAX) = N''; + @deadlock_result nvarchar(MAX) = N'', + @StartDateOriginal datetime = @StartDate, + @EndDateOriginal datetime = @EndDate, + @StartDateUTC datetime, + @EndDateUTC datetime; /*Temporary objects used in the procedure*/ DECLARE @@ -26076,15 +26271,17 @@ BEGIN database_name nvarchar(256), object_name nvarchar(1000), finding_group nvarchar(100), - finding nvarchar(4000) + finding nvarchar(4000), + sort_order bigint ); /*Set these to some sane defaults if NULLs are passed in*/ /*Normally I'd hate this, but we RECOMPILE everything*/ + SELECT @StartDate = - CASE - WHEN @StartDate IS NULL + CASE + WHEN @StartDate IS NULL THEN DATEADD ( @@ -26095,18 +26292,25 @@ BEGIN SYSDATETIME(), GETUTCDATE() ), - ISNULL + DATEADD ( - @StartDate, - DATEADD - ( - DAY, - -7, - SYSDATETIME() - ) + DAY, + -7, + SYSDATETIME() ) ) - ELSE @StartDate + ELSE + DATEADD + ( + MINUTE, + DATEDIFF + ( + MINUTE, + SYSDATETIME(), + GETUTCDATE() + ), + @StartDate + ) END, @EndDate = CASE @@ -26121,15 +26325,62 @@ BEGIN SYSDATETIME(), GETUTCDATE() ), - ISNULL + SYSDATETIME() + ) + ELSE + DATEADD + ( + MINUTE, + DATEDIFF ( - @EndDate, - SYSDATETIME() - ) + MINUTE, + SYSDATETIME(), + GETUTCDATE() + ), + @EndDate ) - ELSE @EndDate END; + SELECT + @StartDateUTC = @StartDate, + @EndDateUTC = @EndDate; + + IF @Azure = 0 + BEGIN + IF NOT EXISTS + ( + SELECT + 1/0 + FROM sys.server_event_sessions AS ses + JOIN sys.dm_xe_sessions AS dxs + ON dxs.name = ses.name + WHERE ses.name = @EventSessionName + AND dxs.create_time IS NOT NULL + ) + BEGIN + RAISERROR('A session with the name %s does not exist or is not currently active.', 11, 1, @EventSessionName) WITH NOWAIT; + RETURN; + END; + END; + + IF @Azure = 1 + BEGIN + IF NOT EXISTS + ( + SELECT + 1/0 + FROM sys.database_event_sessions AS ses + JOIN sys.dm_xe_database_sessions AS dxs + ON dxs.name = ses.name + WHERE ses.name = @EventSessionName + AND dxs.create_time IS NOT NULL + ) + BEGIN + RAISERROR('A session with the name %s does not exist or is not currently active.', 11, 1, @EventSessionName) WITH NOWAIT; + RETURN; + END; + END; + IF @OutputDatabaseName IS NOT NULL BEGIN /*IF databaseName is set, do some sanity checks and put [] around def.*/ SET @d = CONVERT(varchar(40), GETDATE(), 109); @@ -26712,8 +26963,7 @@ BEGIN OR e.x.exist('@name[ .= "database_xml_deadlock_report"]') = 1 OR e.x.exist('@name[ .= "xml_deadlock_report_filtered"]') = 1 ) - AND e.x.exist('@timestamp[. >= sql:variable("@StartDate")]') = 1 - AND e.x.exist('@timestamp[. < sql:variable("@EndDate")]') = 1 + AND e.x.exist('@timestamp[. >= sql:variable("@StartDate") and .< sql:variable("@EndDate")]') = 1 OPTION(RECOMPILE); SET @d = CONVERT(varchar(40), GETDATE(), 109); @@ -26730,9 +26980,9 @@ BEGIN SET @d = CONVERT(varchar(40), GETDATE(), 109); RAISERROR('Inserting to #deadlock_data for event file data', 0, 1) WITH NOWAIT; - IF @Debug = 1 BEGIN SET STATISTICS XML ON; END; + IF @Debug = 1 BEGIN SET STATISTICS XML ON; END; - INSERT + INSERT #deadlock_data WITH(TABLOCKX) ( deadlock_xml @@ -26750,13 +27000,12 @@ BEGIN OR e.x.exist('@name[ .= "database_xml_deadlock_report"]') = 1 OR e.x.exist('@name[ .= "xml_deadlock_report_filtered"]') = 1 ) - AND e.x.exist('@timestamp[. >= sql:variable("@StartDate")]') = 1 - AND e.x.exist('@timestamp[. < sql:variable("@EndDate")]') = 1 + AND e.x.exist('@timestamp[. >= sql:variable("@StartDate") and .< sql:variable("@EndDate")]') = 1 OPTION(RECOMPILE); - IF @Debug = 1 BEGIN SET STATISTICS XML OFF; END; + IF @Debug = 1 BEGIN SET STATISTICS XML OFF; END; - SET @d = CONVERT(varchar(40), GETDATE(), 109); + SET @d = CONVERT(varchar(40), GETDATE(), 109); RAISERROR('Finished at %s', 0, 1, @d) WITH NOWAIT; END; @@ -26787,8 +27036,7 @@ BEGIN ) AS xml CROSS APPLY xml.deadlock_xml.nodes('/event') AS e(x) WHERE 1 = 1 - AND e.x.exist('@timestamp[. >= sql:variable("@StartDate")]') = 1 - AND e.x.exist('@timestamp[. < sql:variable("@EndDate")]') = 1 + AND e.x.exist('@timestamp[. >= sql:variable("@StartDate") and .< sql:variable("@EndDate")]') = 1 OPTION(RECOMPILE); INSERT @@ -26915,7 +27163,12 @@ BEGIN DATEADD ( MINUTE, - DATEDIFF(MINUTE, GETUTCDATE(), SYSDATETIME()), + DATEDIFF + ( + MINUTE, + GETUTCDATE(), + SYSDATETIME() + ), dd.event_date ), dd.victim_id, @@ -26973,6 +27226,7 @@ BEGIN FROM #deadlock_process AS dp CROSS APPLY dp.process_xml.nodes('//executionStack/frame') AS ca(dp) WHERE (ca.dp.exist('@procname[. = sql:variable("@StoredProcName")]') = 1 OR @StoredProcName IS NULL) + AND ca.dp.exist('@sqlhandle[ .= "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"]') = 0 OPTION(RECOMPILE); SET @d = CONVERT(varchar(40), GETDATE(), 109); @@ -27045,7 +27299,7 @@ BEGIN waiter_mode = w.l.value('@mode', 'nvarchar(256)'), owner_id = o.l.value('@id', 'nvarchar(256)'), owner_mode = o.l.value('@mode', 'nvarchar(256)'), - lock_type = CAST(N'OBJECT' AS NVARCHAR(100)) + lock_type = CAST(N'OBJECT' AS nvarchar(100)) INTO #deadlock_owner_waiter FROM ( @@ -27441,13 +27695,19 @@ BEGIN 32 ), step_id = - SUBSTRING - ( - dp.client_app, - CHARINDEX(N': Step ', dp.client_app) + LEN(N': Step '), - CHARINDEX(N')', dp.client_app, CHARINDEX(N': Step ', dp.client_app)) - - (CHARINDEX(N': Step ', dp.client_app) + LEN(N': Step ')) - ) + CASE + WHEN CHARINDEX(N': Step ', dp.client_app) > 0 + AND CHARINDEX(N')', dp.client_app, CHARINDEX(N': Step ', dp.client_app)) > 0 + THEN + SUBSTRING + ( + dp.client_app, + CHARINDEX(N': Step ', dp.client_app) + LEN(N': Step '), + CHARINDEX(N')', dp.client_app, CHARINDEX(N': Step ', dp.client_app)) - + (CHARINDEX(N': Step ', dp.client_app) + LEN(N': Step ')) + ) + ELSE dp.client_app + END FROM #deadlock_process AS dp WHERE dp.client_app LIKE N'SQLAgent - %' AND dp.client_app <> N'SQLAgent - Initial Boot Probe' @@ -27597,6 +27857,14 @@ BEGIN /*Begin checks based on parsed values*/ + /* + First, revert these back since we already converted the event data to local time, + and searches will break if we use the times converted over to UTC for the event data + */ + SELECT + @StartDate = @StartDateOriginal, + @EndDate = @EndDateOriginal; + /*Check 1 is deadlocks by database*/ SET @d = CONVERT(varchar(40), GETDATE(), 109); RAISERROR('Check 1 database deadlocks %s', 0, 1, @d) WITH NOWAIT; @@ -27608,7 +27876,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 1, @@ -27622,7 +27891,10 @@ BEGIN nvarchar(20), COUNT_BIG(DISTINCT dp.event_date) ) + - N' deadlocks.' + N' deadlocks.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT dp.event_date) DESC) FROM #deadlock_process AS dp WHERE 1 = 1 AND (dp.database_name = @DatabaseName OR @DatabaseName IS NULL) @@ -27647,7 +27919,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 2, @@ -27662,7 +27935,10 @@ BEGIN nvarchar(20), COUNT_BIG(DISTINCT dow.event_date) ) + - N' deadlock(s) between read queries and modification queries.' + N' deadlock(s) between read queries and modification queries.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT dow.event_date) DESC) FROM #deadlock_owner_waiter AS dow WHERE 1 = 1 AND dow.lock_mode IN @@ -27701,7 +27977,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 3, @@ -27720,7 +27997,10 @@ BEGIN nvarchar(20), COUNT_BIG(DISTINCT dow.event_date) ) + - N' deadlock(s).' + N' deadlock(s).', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT dow.event_date) DESC) FROM #deadlock_owner_waiter AS dow WHERE 1 = 1 AND (dow.database_id = @DatabaseId OR @DatabaseName IS NULL) @@ -27745,7 +28025,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 3, @@ -27759,7 +28040,10 @@ BEGIN nvarchar(20), COUNT_BIG(DISTINCT dow.event_date) ) + - N' deadlock(s).' + N' deadlock(s).', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT dow.event_date) DESC) FROM #deadlock_owner_waiter AS dow WHERE 1 = 1 AND (dow.database_id = @DatabaseId OR @DatabaseName IS NULL) @@ -27790,7 +28074,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 3, @@ -27804,7 +28089,10 @@ BEGIN nvarchar(20), COUNT_BIG(DISTINCT dow.event_date) ) + - N' deadlock(s).' + N' deadlock(s).', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT dow.event_date) DESC) FROM #deadlock_owner_waiter AS dow WHERE 1 = 1 AND (dow.database_id = @DatabaseId OR @DatabaseName IS NULL) @@ -27834,7 +28122,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 4, @@ -27849,7 +28138,10 @@ BEGIN nvarchar(20), COUNT_BIG(DISTINCT dp.event_date) ) + - N' instances of Serializable deadlocks.' + N' instances of Serializable deadlocks.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT dp.event_date) DESC) FROM #deadlock_process AS dp WHERE dp.isolation_level LIKE N'serializable%' AND (dp.database_name = @DatabaseName OR @DatabaseName IS NULL) @@ -27874,7 +28166,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 5, @@ -27888,7 +28181,10 @@ BEGIN nvarchar(20), COUNT_BIG(DISTINCT dp.event_date) ) + - N' instances of Repeatable Read deadlocks.' + N' instances of Repeatable Read deadlocks.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT dp.event_date) DESC) FROM #deadlock_process AS dp WHERE dp.isolation_level LIKE N'repeatable%' AND (dp.database_name = @DatabaseName OR @DatabaseName IS NULL) @@ -27913,7 +28209,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 6, @@ -27946,7 +28243,10 @@ BEGIN dp.host_name, N'UNKNOWN' ) + - N'.' + N'.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT dp.event_date) DESC) FROM #deadlock_process AS dp WHERE 1 = 1 AND (dp.database_name = @DatabaseName OR @DatabaseName IS NULL) @@ -28027,7 +28327,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 7, @@ -28054,7 +28355,10 @@ BEGIN 1, 1, N'' - ) + N' locks.' + ) + N' locks.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY CONVERT(bigint, lt.lock_count) DESC) FROM lock_types AS lt OPTION(RECOMPILE); @@ -28068,9 +28372,9 @@ BEGIN deadlock_stack AS ( SELECT DISTINCT - ds.id, - ds.proc_name, - ds.event_date, + ds.id, + ds.event_date, + ds.proc_name, database_name = PARSENAME(ds.proc_name, 3), schema_name = @@ -28104,8 +28408,8 @@ BEGIN PARSENAME(ds.proc_name, 3), PARSENAME(ds.proc_name, 2), PARSENAME(ds.proc_name, 1), - ds.id, ds.proc_name, + ds.id, ds.event_date ) INSERT @@ -28137,6 +28441,7 @@ BEGIN AND (dow.event_date >= @StartDate OR @StartDate IS NULL) AND (dow.event_date < @EndDate OR @EndDate IS NULL) AND (dow.object_name = @StoredProcName OR @StoredProcName IS NULL) + AND ds.proc_name NOT LIKE 'Unknown%' OPTION(RECOMPILE); RAISERROR('Finished at %s', 0, 1, @d) WITH NOWAIT; @@ -28209,7 +28514,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 9, @@ -28228,7 +28534,10 @@ BEGIN nvarchar(10), COUNT_BIG(DISTINCT ds.id) ) + - N' deadlocks.' + N' deadlocks.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT ds.id) DESC) FROM #deadlock_stack AS ds JOIN #deadlock_process AS dp ON dp.id = ds.id @@ -28327,19 +28636,19 @@ BEGIN ) ), wait_time_hms = - /*the more wait time you rack up the less accurate this gets, + /*the more wait time you rack up the less accurate this gets, it's either that or erroring out*/ - CASE - WHEN + CASE + WHEN SUM ( CONVERT ( - bigint, + bigint, dp.wait_time ) )/1000 > 2147483647 - THEN + THEN CONVERT ( nvarchar(30), @@ -28352,7 +28661,7 @@ BEGIN ( CONVERT ( - bigint, + bigint, dp.wait_time ) ) @@ -28363,16 +28672,16 @@ BEGIN ), 14 ) - WHEN + WHEN SUM ( CONVERT ( - bigint, + bigint, dp.wait_time ) ) BETWEEN 2147483648 AND 2147483647000 - THEN + THEN CONVERT ( nvarchar(30), @@ -28385,7 +28694,7 @@ BEGIN ( CONVERT ( - bigint, + bigint, dp.wait_time ) ) @@ -28417,7 +28726,9 @@ BEGIN ), 14 ) - END + END, + total_waits = + SUM(CONVERT(bigint, dp.wait_time)) FROM #deadlock_owner_waiter AS dow JOIN #deadlock_process AS dp ON (dp.id = dow.owner_id @@ -28442,7 +28753,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 11, @@ -28463,7 +28775,10 @@ BEGIN cs.wait_time_hms, 14 ) + - N' [dd hh:mm:ss:ms] of deadlock wait time.' + N' [dd hh:mm:ss:ms] of deadlock wait time.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY cs.total_waits DESC) FROM chopsuey AS cs WHERE cs.object_name IS NOT NULL OPTION(RECOMPILE); @@ -28511,7 +28826,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 12, @@ -28534,19 +28850,19 @@ BEGIN ) ) + N' ' + - /*the more wait time you rack up the less accurate this gets, + /*the more wait time you rack up the less accurate this gets, it's either that or erroring out*/ - CASE - WHEN + CASE + WHEN SUM ( CONVERT ( - bigint, + bigint, wt.total_wait_time_ms ) )/1000 > 2147483647 - THEN + THEN CONVERT ( nvarchar(30), @@ -28559,7 +28875,7 @@ BEGIN ( CONVERT ( - bigint, + bigint, wt.total_wait_time_ms ) ) @@ -28570,16 +28886,16 @@ BEGIN ), 14 ) - WHEN + WHEN SUM ( CONVERT ( - bigint, + bigint, wt.total_wait_time_ms ) ) BETWEEN 2147483648 AND 2147483647000 - THEN + THEN CONVERT ( nvarchar(30), @@ -28592,7 +28908,7 @@ BEGIN ( CONVERT ( - bigint, + bigint, wt.total_wait_time_ms ) ) @@ -28624,7 +28940,10 @@ BEGIN ), 14 ) END + - N' [dd hh:mm:ss:ms] of deadlock wait time.' + N' [dd hh:mm:ss:ms] of deadlock wait time.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY SUM(CONVERT(bigint, wt.total_wait_time_ms)) DESC) FROM wait_time AS wt GROUP BY wt.database_name @@ -28643,7 +28962,8 @@ BEGIN database_name, object_name, finding_group, - finding + finding, + sort_order ) SELECT check_id = 13, @@ -28658,7 +28978,10 @@ BEGIN finding = N'There have been ' + RTRIM(COUNT_BIG(DISTINCT aj.event_date)) + - N' deadlocks from this Agent Job and Step.' + N' deadlocks from this Agent Job and Step.', + sort_order = + ROW_NUMBER() + OVER (ORDER BY COUNT_BIG(DISTINCT aj.event_date) DESC) FROM #agent_job AS aj GROUP BY DB_NAME(aj.database_id), @@ -29202,7 +29525,8 @@ BEGIN d.waiter_waiting_to_close, /*end parallel deadlock columns*/ d.deadlock_graph, - d.is_victim + d.is_victim, + d.id INTO #deadlock_results FROM #deadlocks AS d; @@ -29405,26 +29729,202 @@ BEGIN DROP SYNONYM DeadlockFindings; /*done with inserting.*/ END; ELSE /*Output to database is not set output to client app*/ - BEGIN - SET @d = CONVERT(varchar(40), GETDATE(), 109); + BEGIN + SET @d = CONVERT(varchar(40), GETDATE(), 109); RAISERROR('Results to client %s', 0, 1, @d) WITH NOWAIT; - + IF @Debug = 1 BEGIN SET STATISTICS XML ON; END; - + EXEC sys.sp_executesql @deadlock_result; - + IF @Debug = 1 BEGIN SET STATISTICS XML OFF; PRINT @deadlock_result; END; - + RAISERROR('Finished at %s', 0, 1, @d) WITH NOWAIT; - + + SET @d = CONVERT(varchar(40), GETDATE(), 109); + RAISERROR('Getting available execution plans for deadlocks %s', 0, 1, @d) WITH NOWAIT; + + SELECT DISTINCT + available_plans = + 'available_plans', + ds.proc_name, + sql_handle = + CONVERT(varbinary(64), ds.sql_handle, 1), + dow.database_name, + dow.database_id, + dow.object_name, + query_xml = + TRY_CAST(dr.query_xml AS nvarchar(MAX)) + INTO #available_plans + FROM #deadlock_stack AS ds + JOIN #deadlock_owner_waiter AS dow + ON dow.owner_id = ds.id + AND dow.event_date = ds.event_date + JOIN #deadlock_results AS dr + ON dr.id = ds.id + AND dr.event_date = ds.event_date + OPTION(RECOMPILE); + + SELECT + deqs.sql_handle, + deqs.plan_handle, + deqs.statement_start_offset, + deqs.statement_end_offset, + deqs.creation_time, + deqs.last_execution_time, + deqs.execution_count, + total_worker_time_ms = + deqs.total_worker_time / 1000., + avg_worker_time_ms = + CONVERT(decimal(38, 6), deqs.total_worker_time / 1000. / deqs.execution_count), + total_elapsed_time_ms = + deqs.total_elapsed_time / 1000., + avg_elapsed_time = + CONVERT(decimal(38, 6), deqs.total_elapsed_time / 1000. / deqs.execution_count), + executions_per_second = + ISNULL + ( + deqs.execution_count / + NULLIF + ( + DATEDIFF + ( + SECOND, + deqs.creation_time, + deqs.last_execution_time + ), + 0 + ), + 0 + ), + total_physical_reads_mb = + deqs.total_physical_reads * 8. / 1024., + total_logical_writes_mb = + deqs.total_logical_writes * 8. / 1024., + total_logical_reads_mb = + deqs.total_logical_reads * 8. / 1024., + min_grant_mb = + deqs.min_grant_kb * 8. / 1024., + max_grant_mb = + deqs.max_grant_kb * 8. / 1024., + min_used_grant_mb = + deqs.min_used_grant_kb * 8. / 1024., + max_used_grant_mb = + deqs.max_used_grant_kb * 8. / 1024., + deqs.min_reserved_threads, + deqs.max_reserved_threads, + deqs.min_used_threads, + deqs.max_used_threads, + deqs.total_rows + INTO #dm_exec_query_stats + FROM sys.dm_exec_query_stats AS deqs + WHERE EXISTS + ( + SELECT + 1/0 + FROM #available_plans AS ap + WHERE ap.sql_handle = deqs.sql_handle + ) + AND deqs.query_hash IS NOT NULL; + + CREATE CLUSTERED INDEX + deqs + ON #dm_exec_query_stats + ( + sql_handle, + plan_handle + ); + + SELECT + ap.available_plans, + ap.database_name, + query_text = + TRY_CAST(ap.query_xml AS xml), + ap.query_plan, + ap.creation_time, + ap.last_execution_time, + ap.execution_count, + ap.executions_per_second, + ap.total_worker_time_ms, + ap.avg_worker_time_ms, + ap.total_elapsed_time_ms, + ap.avg_elapsed_time, + ap.total_logical_reads_mb, + ap.total_physical_reads_mb, + ap.total_logical_writes_mb, + ap.min_grant_mb, + ap.max_grant_mb, + ap.min_used_grant_mb, + ap.max_used_grant_mb, + ap.min_reserved_threads, + ap.max_reserved_threads, + ap.min_used_threads, + ap.max_used_threads, + ap.total_rows, + ap.sql_handle, + ap.statement_start_offset, + ap.statement_end_offset + FROM + ( + + SELECT + ap.*, + c.statement_start_offset, + c.statement_end_offset, + c.creation_time, + c.last_execution_time, + c.execution_count, + c.total_worker_time_ms, + c.avg_worker_time_ms, + c.total_elapsed_time_ms, + c.avg_elapsed_time, + c.executions_per_second, + c.total_physical_reads_mb, + c.total_logical_writes_mb, + c.total_logical_reads_mb, + c.min_grant_mb, + c.max_grant_mb, + c.min_used_grant_mb, + c.max_used_grant_mb, + c.min_reserved_threads, + c.max_reserved_threads, + c.min_used_threads, + c.max_used_threads, + c.total_rows, + c.query_plan + FROM #available_plans AS ap + OUTER APPLY + ( + SELECT + deqs.*, + query_plan = + TRY_CAST(deps.query_plan AS xml) + FROM #dm_exec_query_stats deqs + OUTER APPLY sys.dm_exec_text_query_plan + ( + deqs.plan_handle, + deqs.statement_start_offset, + deqs.statement_end_offset + ) AS deps + WHERE deqs.sql_handle = ap.sql_handle + AND deps.dbid = ap.database_id + ) AS c + ) AS ap + WHERE ap.query_plan IS NOT NULL + ORDER BY + ap.avg_worker_time_ms DESC + OPTION(RECOMPILE, LOOP JOIN, HASH JOIN); + + RAISERROR('Finished at %s', 0, 1, @d) WITH NOWAIT; + SET @d = CONVERT(varchar(40), GETDATE(), 109); RAISERROR('Returning findings %s', 0, 1, @d) WITH NOWAIT; - + SELECT df.check_id, df.database_name, @@ -29432,26 +29932,28 @@ BEGIN df.finding_group, df.finding FROM #deadlock_findings AS df - ORDER BY df.check_id + ORDER BY + df.check_id, + df.sort_order OPTION(RECOMPILE); - + SET @d = CONVERT(varchar(40), GETDATE(), 109); RAISERROR('Finished at %s', 0, 1, @d) WITH NOWAIT; END; /*done with output to client app.*/ - END; + END; IF @Debug = 1 BEGIN SELECT - table_name = N'#dd', + table_name = N'#deadlock_data', * - FROM #dd AS d + FROM #deadlock_data AS dd OPTION(RECOMPILE); SELECT - table_name = N'#deadlock_data', + table_name = N'#dd', * - FROM #deadlock_data AS dd + FROM #dd AS d OPTION(RECOMPILE); SELECT @@ -29508,9 +30010,111 @@ BEGIN FROM @sysAssObjId AS s OPTION(RECOMPILE); + SELECT + table_name = N'#available_plans', + * + FROM #available_plans AS ap + OPTION(RECOMPILE); + + SELECT + table_name = N'#dm_exec_query_stats', + * + FROM #dm_exec_query_stats + OPTION(RECOMPILE); + + SELECT + procedure_parameters = + 'procedure_parameters', + DatabaseName = + @DatabaseName, + StartDate = + @StartDate, + EndDate = + @EndDate, + ObjectName = + @ObjectName, + StoredProcName = + @StoredProcName, + AppName = + @AppName, + HostName = + @HostName, + LoginName = + @LoginName, + EventSessionName = + @EventSessionName, + TargetSessionType = + @TargetSessionType, + VictimsOnly = + @VictimsOnly, + Debug = + @Debug, + Help = + @Help, + Version = + @Version, + VersionDate = + @VersionDate, + VersionCheckMode = + @VersionCheckMode, + OutputDatabaseName = + @OutputDatabaseName, + OutputSchemaName = + @OutputSchemaName, + OutputTableName = + @OutputTableName, + ExportToExcel = + @ExportToExcel; + + SELECT + declared_variables = + 'declared_variables', + DatabaseId = + @DatabaseId, + StartDateUTC = + @StartDateUTC, + EndDateUTC = + @EndDateUTC, + ProductVersion = + @ProductVersion, + ProductVersionMajor = + @ProductVersionMajor, + ProductVersionMinor = + @ProductVersionMinor, + ObjectFullName = + @ObjectFullName, + Azure = + @Azure, + RDS = + @RDS, + d = + @d, + StringToExecute = + @StringToExecute, + StringToExecuteParams = + @StringToExecuteParams, + r = + @r, + OutputTableFindings = + @OutputTableFindings, + DeadlockCount = + @DeadlockCount, + ServerName = + @ServerName, + OutputDatabaseCheck = + @OutputDatabaseCheck, + SessionId = + @SessionId, + TargetSessionId = + @TargetSessionId, + FileName = + @FileName, + inputbuf_bom = + @inputbuf_bom, + deadlock_result = + @deadlock_result; END; /*End debug*/ END; /*Final End*/ - GO SET ANSI_NULLS ON; SET ANSI_PADDING ON; @@ -29571,7 +30175,7 @@ SET NOCOUNT ON; SET STATISTICS XML OFF; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -SELECT @Version = '8.15', @VersionDate = '20230613'; +SELECT @Version = '8.16', @VersionDate = '20230820'; IF(@VersionCheckMode = 1) BEGIN RETURN; @@ -35302,7 +35906,7 @@ BEGIN SET STATISTICS XML OFF; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; - SELECT @Version = '8.15', @VersionDate = '20230613'; + SELECT @Version = '8.16', @VersionDate = '20230820'; IF(@VersionCheckMode = 1) BEGIN @@ -36698,12 +37302,17 @@ DELETE FROM dbo.SqlServerVersions; INSERT INTO dbo.SqlServerVersions (MajorVersionNumber, MinorVersionNumber, Branch, [Url], ReleaseDate, MainstreamSupportEndDate, ExtendedSupportEndDate, MajorVersionName, MinorVersionName) VALUES + (16, 4065, 'CU7', 'https://support.microsoft.com/en-us/help/5028743', '2023-08-10', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'Cumulative Update 7'), + (16, 4055, 'CU6', 'https://support.microsoft.com/en-us/help/5027505', '2023-07-13', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'Cumulative Update 6'), + (16, 4045, 'CU5', 'https://support.microsoft.com/en-us/help/5026806', '2023-06-15', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'Cumulative Update 5'), (16, 4035, 'CU4', 'https://support.microsoft.com/en-us/help/5026717', '2023-05-11', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'Cumulative Update 4'), (16, 4025, 'CU3', 'https://support.microsoft.com/en-us/help/5024396', '2023-04-13', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'Cumulative Update 3'), (16, 4015, 'CU2', 'https://support.microsoft.com/en-us/help/5023127', '2023-03-15', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'Cumulative Update 2'), (16, 4003, 'CU1', 'https://support.microsoft.com/en-us/help/5022375', '2023-02-16', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'Cumulative Update 1'), (16, 1050, 'RTM GDR', 'https://support.microsoft.com/kb/5021522', '2023-02-14', '2028-01-11', '2033-01-11', 'SQL Server 2022 GDR', 'RTM'), (16, 1000, 'RTM', '', '2022-11-15', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'RTM'), + (15, 4322, 'CU22', 'https://support.microsoft.com/kb/5027702', '2023-08-14', '2025-01-07', '2030-01-08', 'SQL Server 2019', 'Cumulative Update 22'), + (15, 4316, 'CU21', 'https://support.microsoft.com/kb/5025808', '2023-06-15', '2025-01-07', '2030-01-08', 'SQL Server 2019', 'Cumulative Update 21'), (15, 4312, 'CU20', 'https://support.microsoft.com/kb/5024276', '2023-04-13', '2025-01-07', '2030-01-08', 'SQL Server 2019', 'Cumulative Update 20'), (15, 4298, 'CU19', 'https://support.microsoft.com/kb/5023049', '2023-02-16', '2025-01-07', '2030-01-08', 'SQL Server 2019', 'Cumulative Update 19'), (15, 4280, 'CU18 GDR', 'https://support.microsoft.com/kb/5021124', '2023-02-14', '2025-01-07', '2030-01-08', 'SQL Server 2019', 'Cumulative Update 18 GDR'), @@ -37126,7 +37735,7 @@ SET NOCOUNT ON; SET STATISTICS XML OFF; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -SELECT @Version = '8.15', @VersionDate = '20230613'; +SELECT @Version = '8.16', @VersionDate = '20230820'; IF(@VersionCheckMode = 1) BEGIN diff --git a/SqlServerVersions.sql b/SqlServerVersions.sql index 0b5f69ae..cf6d224c 100644 --- a/SqlServerVersions.sql +++ b/SqlServerVersions.sql @@ -50,6 +50,7 @@ VALUES (16, 4003, 'CU1', 'https://support.microsoft.com/en-us/help/5022375', '2023-02-16', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'Cumulative Update 1'), (16, 1050, 'RTM GDR', 'https://support.microsoft.com/kb/5021522', '2023-02-14', '2028-01-11', '2033-01-11', 'SQL Server 2022 GDR', 'RTM'), (16, 1000, 'RTM', '', '2022-11-15', '2028-01-11', '2033-01-11', 'SQL Server 2022', 'RTM'), + (15, 4322, 'CU22', 'https://support.microsoft.com/kb/5027702', '2023-08-14', '2025-01-07', '2030-01-08', 'SQL Server 2019', 'Cumulative Update 22'), (15, 4316, 'CU21', 'https://support.microsoft.com/kb/5025808', '2023-06-15', '2025-01-07', '2030-01-08', 'SQL Server 2019', 'Cumulative Update 21'), (15, 4312, 'CU20', 'https://support.microsoft.com/kb/5024276', '2023-04-13', '2025-01-07', '2030-01-08', 'SQL Server 2019', 'Cumulative Update 20'), (15, 4298, 'CU19', 'https://support.microsoft.com/kb/5023049', '2023-02-16', '2025-01-07', '2030-01-08', 'SQL Server 2019', 'Cumulative Update 19'), diff --git a/sp_AllNightLog.sql b/sp_AllNightLog.sql index 5c4e7219..0a37f4f7 100644 --- a/sp_AllNightLog.sql +++ b/sp_AllNightLog.sql @@ -31,7 +31,7 @@ SET STATISTICS XML OFF; BEGIN; -SELECT @Version = '8.15', @VersionDate = '20230613'; +SELECT @Version = '8.16', @VersionDate = '20230820'; IF(@VersionCheckMode = 1) BEGIN diff --git a/sp_AllNightLog_Setup.sql b/sp_AllNightLog_Setup.sql index a1ddcec3..9df6a50c 100644 --- a/sp_AllNightLog_Setup.sql +++ b/sp_AllNightLog_Setup.sql @@ -38,7 +38,7 @@ SET STATISTICS XML OFF; BEGIN; -SELECT @Version = '8.15', @VersionDate = '20230613'; +SELECT @Version = '8.16', @VersionDate = '20230820'; IF(@VersionCheckMode = 1) BEGIN diff --git a/sp_Blitz.sql b/sp_Blitz.sql index c3190203..552df1ee 100644 --- a/sp_Blitz.sql +++ b/sp_Blitz.sql @@ -38,7 +38,7 @@ AS SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; - SELECT @Version = '8.15', @VersionDate = '20230613'; + SELECT @Version = '8.16', @VersionDate = '20230820'; SET @OutputType = UPPER(@OutputType); IF(@VersionCheckMode = 1) diff --git a/sp_BlitzAnalysis.sql b/sp_BlitzAnalysis.sql index 5c686a69..9cfb737c 100644 --- a/sp_BlitzAnalysis.sql +++ b/sp_BlitzAnalysis.sql @@ -37,7 +37,7 @@ AS SET NOCOUNT ON; SET STATISTICS XML OFF; -SELECT @Version = '8.15', @VersionDate = '20230613'; +SELECT @Version = '8.16', @VersionDate = '20230820'; IF(@VersionCheckMode = 1) BEGIN diff --git a/sp_BlitzBackups.sql b/sp_BlitzBackups.sql index c5d4932a..17d08190 100755 --- a/sp_BlitzBackups.sql +++ b/sp_BlitzBackups.sql @@ -24,7 +24,7 @@ AS SET STATISTICS XML OFF; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; - SELECT @Version = '8.15', @VersionDate = '20230613'; + SELECT @Version = '8.16', @VersionDate = '20230820'; IF(@VersionCheckMode = 1) BEGIN diff --git a/sp_BlitzCache.sql b/sp_BlitzCache.sql index 10d3013f..ddd418e2 100644 --- a/sp_BlitzCache.sql +++ b/sp_BlitzCache.sql @@ -281,7 +281,7 @@ SET NOCOUNT ON; SET STATISTICS XML OFF; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -SELECT @Version = '8.15', @VersionDate = '20230613'; +SELECT @Version = '8.16', @VersionDate = '20230820'; SET @OutputType = UPPER(@OutputType); IF(@VersionCheckMode = 1) diff --git a/sp_BlitzFirst.sql b/sp_BlitzFirst.sql index 5cfc95b0..0e822f1b 100644 --- a/sp_BlitzFirst.sql +++ b/sp_BlitzFirst.sql @@ -47,7 +47,7 @@ SET NOCOUNT ON; SET STATISTICS XML OFF; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -SELECT @Version = '8.15', @VersionDate = '20230613'; +SELECT @Version = '8.16', @VersionDate = '20230820'; IF(@VersionCheckMode = 1) BEGIN diff --git a/sp_BlitzInMemoryOLTP.sql b/sp_BlitzInMemoryOLTP.sql index 8dde49d8..919a72d6 100644 --- a/sp_BlitzInMemoryOLTP.sql +++ b/sp_BlitzInMemoryOLTP.sql @@ -82,7 +82,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI */ AS DECLARE @ScriptVersion VARCHAR(30); -SELECT @ScriptVersion = '1.8', @VersionDate = '20230613'; +SELECT @ScriptVersion = '1.8', @VersionDate = '20230820'; IF(@VersionCheckMode = 1) BEGIN diff --git a/sp_BlitzIndex.sql b/sp_BlitzIndex.sql index 5ad1e33a..2c83589d 100644 --- a/sp_BlitzIndex.sql +++ b/sp_BlitzIndex.sql @@ -48,7 +48,7 @@ SET NOCOUNT ON; SET STATISTICS XML OFF; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -SELECT @Version = '8.15', @VersionDate = '20230613'; +SELECT @Version = '8.16', @VersionDate = '20230820'; SET @OutputType = UPPER(@OutputType); IF(@VersionCheckMode = 1) diff --git a/sp_BlitzLock.sql b/sp_BlitzLock.sql index b609731f..6eb00b83 100644 --- a/sp_BlitzLock.sql +++ b/sp_BlitzLock.sql @@ -35,7 +35,7 @@ BEGIN SET NOCOUNT, XACT_ABORT ON; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; - SELECT @Version = '8.15', @VersionDate = '20230613'; + SELECT @Version = '8.16', @VersionDate = '20230820'; IF @VersionCheckMode = 1 BEGIN diff --git a/sp_BlitzQueryStore.sql b/sp_BlitzQueryStore.sql index 43171140..c600a130 100644 --- a/sp_BlitzQueryStore.sql +++ b/sp_BlitzQueryStore.sql @@ -57,7 +57,7 @@ SET NOCOUNT ON; SET STATISTICS XML OFF; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -SELECT @Version = '8.15', @VersionDate = '20230613'; +SELECT @Version = '8.16', @VersionDate = '20230820'; IF(@VersionCheckMode = 1) BEGIN RETURN; diff --git a/sp_BlitzWho.sql b/sp_BlitzWho.sql index 5a65a607..d433ddf5 100644 --- a/sp_BlitzWho.sql +++ b/sp_BlitzWho.sql @@ -33,7 +33,7 @@ BEGIN SET STATISTICS XML OFF; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; - SELECT @Version = '8.15', @VersionDate = '20230613'; + SELECT @Version = '8.16', @VersionDate = '20230820'; IF(@VersionCheckMode = 1) BEGIN diff --git a/sp_DatabaseRestore.sql b/sp_DatabaseRestore.sql index 06968cad..646ed84e 100755 --- a/sp_DatabaseRestore.sql +++ b/sp_DatabaseRestore.sql @@ -45,7 +45,7 @@ SET STATISTICS XML OFF; /*Versioning details*/ -SELECT @Version = '8.15', @VersionDate = '20230613'; +SELECT @Version = '8.16', @VersionDate = '20230820'; IF(@VersionCheckMode = 1) BEGIN diff --git a/sp_ineachdb.sql b/sp_ineachdb.sql index 8ea9646e..8e7890e8 100644 --- a/sp_ineachdb.sql +++ b/sp_ineachdb.sql @@ -35,7 +35,7 @@ BEGIN SET NOCOUNT ON; SET STATISTICS XML OFF; - SELECT @Version = '8.15', @VersionDate = '20230613'; + SELECT @Version = '8.16', @VersionDate = '20230820'; IF(@VersionCheckMode = 1) BEGIN