死锁 - 从死锁报告中查找完整的 SQL 语句

Deadlocks - Finding full SQL statement from deadlock report

我有一个针对 SQL Server 2016 数据库的应用程序 运行 导致死锁。

死锁图显示了底层对象的 Objectid 和对象名称。它还提供了截断的 SQL 语句。

但是,有些语句非常大,超出了 <inputbuf> 实体的 XML 死锁报告中看似 1024 个字符的限制。我希望能够查看这些锁中涉及的完整语句,以便重现和调试问题。

有没有办法增加这个限制,或者有没有办法在给定死锁图中包含的事务描述符的情况下找到完整的 SQL 语句?

如果您使用的是存储过程等代码对象,则可以使用第三方工具轻松找到它们。 一些在过去为我节省了大量时间的免费软件。 SSMS 的免费插件。

或者你可以通过这个代码找到-

-- =============================================
-- Author:      Sharon
-- Create date: 12/10/2014
-- Description: FindObject
-- =============================================
ALTER PROCEDURE [dbo].[usp_FindObject]
    @Text         NVARCHAR(4000),
    @DatabaseName sysname,
    @Table        BIT = 1,
    @Column       BIT = 1,
    @View         BIT = 1,
    @Function     BIT = 1,
    @Procedure    BIT = 1,
    @Trigger      BIT = 1,
    @Constreint   BIT = 1,
    @Job          BIT = 0,
    @ReportServer BIT = 0,
    @IsExactMatch BIT = 0
AS
BEGIN
    SET NOCOUNT ON;


    DECLARE @IsAllDatabases BIT = 0;
    DECLARE @Print NVARCHAR(2048) = N'';
    DECLARE @cmd NVARCHAR(MAX) = N'';



    IF @Text IS NULL
        RAISERROR('Insert text to look for!', 16, 1);

    IF @IsExactMatch IS NULL
        SET @IsExactMatch = 0;
    IF IIF(@Table IS NULL, 0, @Table) + IIF(@Column IS NULL, 0, @Column) + IIF(@View IS NULL, 0, @View)
       + IIF(@Function IS NULL, 0, @Function) + IIF(@Procedure IS NULL, 0, @Procedure)
       + IIF(@Trigger IS NULL, 0, @Trigger) + IIF(@Constreint IS NULL, 0, @Constreint) > 0
    BEGIN
        IF NOT EXISTS (   SELECT    TOP (1)  1
                          FROM      sys.databases
                          WHERE     name = @DatabaseName)
        BEGIN
            SET @IsAllDatabases = 1;
            CREATE TABLE #DBResult
                 (   [Database Name] sysname       NULL,
                     [Object Schema] sysname       NULL,
                     [Object Name]   sysname       NULL,
                     [Object Type]   sysname       NULL,
                     [TEXT Location] NVARCHAR(MAX) NULL,
                     [Position]      NVARCHAR(1000)
                 );
            DECLARE curDatabases CURSOR LOCAL FAST_FORWARD READ_ONLY FOR 
            SELECT  d.name
            FROM    master.sys.databases d
            WHERE   d.state = 0 --ONLINE
                    AND d.database_id > 4
                    AND HAS_DBACCESS(d.name) = 1  -- Have access
                    AND DATABASEPROPERTYEX(d.name, 'Updateability') = 'READ_WRITE'
                    AND NOT EXISTS
                      (
                        SELECT  TOP (1) 1 
                        FROM    sys.dm_hadr_database_replica_states AS drs 
                                INNER JOIN sys.availability_replicas AS ar ON ar.replica_id = drs.replica_id
                                INNER JOIN sys.dm_hadr_availability_group_states ags ON ags.group_id = ar.group_id
                          WHERE drs.database_id = d.database_id
                                AND ar.secondary_role_allow_connections = 0
                                AND ags.primary_replica <> CONVERT(sysname, SERVERPROPERTY('ServerName'))
                      );
        END;
        ELSE
        BEGIN
            
            DECLARE curDatabases CURSOR LOCAL FAST_FORWARD READ_ONLY FOR 
            SELECT  d.name
            FROM    master.sys.databases d
            WHERE   d.state = 0 --ONLINE
                    AND d.database_id > 4
                    AND HAS_DBACCESS(d.name) = 1  -- Have access
                    AND DATABASEPROPERTYEX(d.name, 'Updateability') = 'READ_WRITE'
                    AND NOT EXISTS
                      (
                        SELECT  TOP (1) 1 
                        FROM    sys.dm_hadr_database_replica_states AS drs 
                                INNER JOIN sys.availability_replicas AS ar ON ar.replica_id = drs.replica_id
                                INNER JOIN sys.dm_hadr_availability_group_states ags ON ags.group_id = ar.group_id
                          WHERE drs.database_id = d.database_id
                                AND ar.secondary_role_allow_connections = 0
                                AND ags.primary_replica <> CONVERT(sysname, SERVERPROPERTY('ServerName'))
                      )
                    AND d.name = @DatabaseName;
            CREATE TABLE #Result
                 (
                     [Object Schema] sysname       NULL,
                     [Object Name]   sysname       NULL,
                     [Object Type]   sysname       NULL,
                     [TEXT Location] NVARCHAR(MAX) NULL,
                     [Position]      NVARCHAR(1000)
                 );
        END
            -- Table names
            IF @Table = 1
            BEGIN



                OPEN curDatabases;

                FETCH NEXT FROM curDatabases INTO @DatabaseName;

                WHILE @@FETCH_STATUS = 0
                BEGIN
                SET @cmd = CONCAT(IIF(@DatabaseName IS NOT NULL,N'USE [' + @DatabaseName + '];',N''),N'
INSERT #',IIF(@IsAllDatabases = 1,'DB',''),'Result
SELECT ',IIF(@IsAllDatabases = 1,'DB_NAME(),',''),'TABLE_SCHEMA  AS [Object Schema]
        ,TABLE_NAME    AS [Object Name]
        ,TABLE_TYPE    AS [Object Type]
        ,''Table Name''  AS [TEXT Location]
        ,NULL
FROM    INFORMATION_SCHEMA.TABLES
WHERE   TABLE_TYPE = N''BASE TABLE''
        AND  TABLE_NAME LIKE ''%''+@Text+''%''');
                    EXEC sp_executesql @cmd, N'@Text nvarchar(4000)', @Text = @Text;
                    SET @Print = CONCAT('Search Table on ',@DatabaseName,' complete.');
                    RAISERROR (@Print, 10, 1) WITH NOWAIT;
                    FETCH NEXT FROM curDatabases INTO @DatabaseName;
                END

                CLOSE curDatabases;
                
            END;

            --Column names| computed_columns
            IF @Column = 1
            BEGIN

                OPEN curDatabases;

                FETCH NEXT FROM curDatabases INTO @DatabaseName;

                WHILE @@FETCH_STATUS = 0
                BEGIN

                SET @cmd = CONCAT(IIF(@DatabaseName IS NOT NULL,N'USE [' + @DatabaseName + '];',N''),N'
INSERT #',IIF(@IsAllDatabases = 1,'DB',''),'Result
SELECT  ',IIF(@IsAllDatabases = 1,'DB_NAME(),',''),'TABLE_SCHEMA AS [Object Schema]
        ,TABLE_NAME   AS [Object Name]
        ,''COLUMN''      AS [Object Type]
        ,COLUMN_NAME AS [TEXT Location]
        ,NULL
FROM  INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME LIKE ''%''+@Text+''%''
UNION ALL 

SELECT  ',IIF(@IsAllDatabases = 1,'DB_NAME(),',''),'s.name AS [Object Schema]
        ,o.name       AS [Object Name]
        ,o.type_desc       AS [Object Type]
        ,C.definition AS [TEXT Location]
        ,NULL
FROM    sys.computed_columns C
        INNER JOIN sys.objects o ON C.object_id = o.object_id
        INNER JOIN sys.schemas s ON s.schema_id = o.schema_id
WHERE   C.definition Like ''%''+@Text+''%''
')              ;

                    EXEC sp_executesql @cmd, N'@Text nvarchar(4000)', @Text = @Text;
                    SET @Print = CONCAT('Search Column on ',@DatabaseName,' complete.');
                    RAISERROR (@Print, 10, 1) WITH NOWAIT;

                    FETCH NEXT FROM curDatabases INTO @DatabaseName;
                END

                CLOSE curDatabases;
            END;

            --PROCEDURE
            IF @Procedure = 1
            BEGIN
            
                OPEN curDatabases;

                FETCH NEXT FROM curDatabases INTO @DatabaseName;

                WHILE @@FETCH_STATUS = 0
                BEGIN

                SET @cmd = CONCAT(
                               N'INSERT #',IIF(@IsAllDatabases = 1,'DB',''),'Result
SELECT  ',IIF(@IsAllDatabases = 1,'DB_NAME(),',''),'s.name AS [Object Schema]
        ,o.name       AS [Object Name]
        ,''PROCEDURE'' AS [Object Type]
        ,m.definition AS [TEXT Location]
        ,SUBSTRING(m.definition,PATINDEX(''%''+@Text+''%'',m.definition),100)
FROM    sys.sql_modules m 
        INNER JOIN sys.objects o ON m.object_id=o.object_id
        INNER JOIN sys.schemas s ON s.schema_id = o.schema_id
WHERE   m.definition Like ''%''+@Text+''%''
        and o.type = ''P''');

                    EXEC sp_executesql @cmd, N'@Text nvarchar(4000)', @Text = @Text;
                    SET @Print = CONCAT('Search Procedure on ',@DatabaseName,' complete.');
                    RAISERROR (@Print, 10, 1) WITH NOWAIT;

                    FETCH NEXT FROM curDatabases INTO @DatabaseName;
                END

                CLOSE curDatabases;
            END;


            -- FUNCTION
            IF @Function = 1
            BEGIN

                OPEN curDatabases;

                FETCH NEXT FROM curDatabases INTO @DatabaseName;

                WHILE @@FETCH_STATUS = 0
                BEGIN

                SET @cmd = CONCAT(
                               N'INSERT #',IIF(@IsAllDatabases = 1,'DB',''),'Result
SELECT  ',IIF(@IsAllDatabases = 1,'DB_NAME(),',''),'s.name AS [Object Schema]
        ,o.name       AS [Object Name]
        ,''FUNCTION('' + o.type + '')'' COLLATE SQL_Latin1_General_CP1_CI_AS AS [Object Type]
        ,m.definition AS [TEXT Location]
        ,SUBSTRING(m.definition,PATINDEX(''%''+@Text+''%'',m.definition),100)
FROM    sys.sql_modules m 
        INNER JOIN sys.objects o ON m.object_id = o.object_id
        INNER JOIN sys.schemas s ON s.schema_id = o.schema_id
WHERE   m.definition Like ''%''+@Text+''%''
        and o.type in (''FN'',''AF'',''FS'',''FT'',''IF'',''TF'')');

                    EXEC sp_executesql @cmd, N'@Text nvarchar(4000)', @Text = @Text;
                    SET @Print = CONCAT('Search Function on ',@DatabaseName,' complete.');
                    RAISERROR (@Print, 10, 1) WITH NOWAIT;

                    FETCH NEXT FROM curDatabases INTO @DatabaseName;
                END

                CLOSE curDatabases;
            END;
            -- Trigger
            IF @Trigger = 1
            BEGIN
            
                OPEN curDatabases;

                FETCH NEXT FROM curDatabases INTO @DatabaseName;

                WHILE @@FETCH_STATUS = 0
                BEGIN

                SET @cmd = CONCAT(
                               N'INSERT #',IIF(@IsAllDatabases = 1,'DB',''),'Result
SELECT  ',IIF(@IsAllDatabases = 1,'DB_NAME(),',''),'s.name AS [Object Schema]
        ,o.name       AS [Object Name]
        ,''FUNCTION('' + o.type + '')'' COLLATE SQL_Latin1_General_CP1_CI_AS AS [Object Type]
        ,m.definition AS [TEXT Location]
        ,SUBSTRING(m.definition,PATINDEX(''%''+@Text+''%'',m.definition),100)
FROM    sys.sql_modules m 
        INNER JOIN sys.objects o ON m.object_id = o.object_id
        INNER JOIN sys.schemas s ON s.schema_id = o.schema_id
WHERE   m.definition Like ''%''+@Text+''%''
        and o.type = ''TR''');

                    EXEC sp_executesql @cmd, N'@Text nvarchar(4000)', @Text = @Text;
                    SET @Print = CONCAT('Search Trigger on ',@DatabaseName,' complete.');
                    RAISERROR (@Print, 10, 1) WITH NOWAIT;

                    FETCH NEXT FROM curDatabases INTO @DatabaseName;
                END

                CLOSE curDatabases;
            END;
            --View
            IF @View = 1
            BEGIN
            
                OPEN curDatabases;

                FETCH NEXT FROM curDatabases INTO @DatabaseName;

                WHILE @@FETCH_STATUS = 0
                BEGIN

                SET @cmd = CONCAT(
                               N'INSERT #',IIF(@IsAllDatabases = 1,'DB',''),'Result
SELECT  ',IIF(@IsAllDatabases = 1,'DB_NAME(),',''),'s.name AS [Object Schema]
        ,o.name       AS [Object Name]
        ,o.type_desc COLLATE SQL_Latin1_General_CP1_CI_AS AS [Object Type]
        ,m.definition AS [TEXT Location]
        ,SUBSTRING(m.definition,PATINDEX(''%''+@Text+''%'',m.definition),100)
FROM    sys.sql_modules m 
        INNER JOIN sys.objects o ON m.object_id = o.object_id
        INNER JOIN sys.schemas s ON s.schema_id = o.schema_id
WHERE   m.definition Like ''%''+@Text+''%''
        and o.type = ''v''');

                    EXEC sp_executesql @cmd, N'@Text nvarchar(4000)', @Text = @Text;
                    SET @Print = CONCAT('Search View on ',@DatabaseName,' complete.');
                    RAISERROR (@Print, 10, 1) WITH NOWAIT;

                    FETCH NEXT FROM curDatabases INTO @DatabaseName;
                END

                CLOSE curDatabases;
            END;

            --default_constraints| check_constraints
            IF @Constreint = 1
            BEGIN
            
                OPEN curDatabases;

                FETCH NEXT FROM curDatabases INTO @DatabaseName;

                WHILE @@FETCH_STATUS = 0
                BEGIN

                SET @cmd = CONCAT(
                               N'INSERT #',IIF(@IsAllDatabases = 1,'DB',''),'Result
SELECT  ',IIF(@IsAllDatabases = 1,'DB_NAME(),',''),'s.name AS [Object Schema]
        ,o.name       AS [Object Name]
        ,o.type_desc       AS [Object Type]
        ,D.definition AS [TEXT Location]
        ,SUBSTRING(D.definition,PATINDEX(''%''+@Text+''%'',D.definition),100)
FROM    sys.default_constraints D
        INNER JOIN sys.objects o ON D.object_id = o.object_id
        INNER JOIN sys.schemas s ON s.schema_id = o.schema_id
WHERE   D.definition Like ''%''+@Text+''%''
UNION ALL 
SELECT  ',IIF(@IsAllDatabases = 1,'DB_NAME(),',''),'s.name AS [Object Schema]
        ,o.name       AS [Object Name]
        ,o.type_desc       AS [Object Type]
        ,C.definition AS [TEXT Location]
        ,SUBSTRING(D.definition,PATINDEX(''%''+@Text+''%'',C.definition),100)
FROM    sys.check_constraints C
        INNER JOIN sys.objects o ON C.object_id = o.object_id
        INNER JOIN sys.schemas s ON s.schema_id = o.schema_id
WHERE   C.definition Like ''%''+@Text+''%''');

                    EXEC sp_executesql @cmd, N'@Text nvarchar(4000)', @Text = @Text;
                    SET @Print = CONCAT('Search Constreint on ',@DatabaseName,' complete.');
                    RAISERROR (@Print, 10, 1) WITH NOWAIT;

                    FETCH NEXT FROM curDatabases INTO @DatabaseName;
                END

                CLOSE curDatabases;
            END;


            
            IF OBJECT_ID('tempdb..#DBResult') IS NOT NULL
            BEGIN
                SELECT  *
                FROM    #DBResult
                WHERE   @IsExactMatch = 0
                        OR (@IsExactMatch = 1 AND [Object Name] = @Text);
                DROP TABLE #DBResult;
            END 
            IF OBJECT_ID('tempdb..#Result') IS NOT NULL
            BEGIN
                SELECT  *
                FROM    #Result
                WHERE   @IsExactMatch = 0
                        OR (@IsExactMatch = 1 AND [Object Name] = @Text);
                DROP TABLE #Result;
            END

        END;
    END;
    -- Job
    IF @Job = 1
    BEGIN
        DECLARE @PreviewTextSize INT = 100;

        SELECT  'Job Steps'                AS SearchType,
                j.[name]                   AS [Job Name],
                s.step_id                  AS [Step #],
                REPLACE(
                    REPLACE(
                        SUBSTRING(s.command, CHARINDEX(@Text, s.command) - @PreviewTextSize / 2, @PreviewTextSize),
                        CHAR(13) + CHAR(10),
                        ''),
                    @Text,
                    '***' + @Text + '***') AS Command
        FROM    msdb.dbo.sysjobs                j
                INNER JOIN msdb.dbo.sysjobsteps s ON j.job_id = s.job_id
        WHERE   s.command LIKE '%' + @Text + '%';
    END;
    -- SSRS
    IF @ReportServer = 1
    BEGIN
        IF EXISTS (SELECT TOP (1) 1 FROM sys.databases WHERE name = 'ReportServer')
        BEGIN;
            WITH cte AS
                (
                --gets the RDL; note the double convert.
                SELECT  [Path],
                        [Name]                                         AS Report_Name,
                        CONVERT(XML, CONVERT(VARBINARY(MAX), Content)) AS rdl
                FROM    ReportServer.dbo.Catalog)
            SELECT      LEFT([Path], LEN([Path]) - CHARINDEX('/', REVERSE([Path])) + 1)           AS Report_Path,
                        Report_Name,
                        T1.N.value('@Name', 'nvarchar(128)')                                      AS DataSetName,
                        T2.N.value('(*:DataSourceName/text())[1]', 'nvarchar(128)')               AS DataSourceName,
                        ISNULL(T2.N.value('(*:CommandType/text())[1]', 'nvarchar(128)'), 'T-SQL') AS CommandType,
                        T2.N.value('(*:CommandText/text())[1]', 'nvarchar(max)')                  AS CommandText
            INTO        #SSRS
            FROM        cte                                                       AS T
                        CROSS APPLY T.rdl.nodes('/*:Report/*:DataSets/*:DataSet') AS T1(N)
                        CROSS APPLY T1.N.nodes('*:Query') AS T2(N)
            ORDER BY    Report_Path,
                        Report_Name,
                        DataSetName,
                        DataSourceName,
                        CommandType,
                        CommandText;

            SELECT  *
            FROM    #SSRS
            WHERE   CommandText LIKE '%' + @Text + '%';
        END;
        
    DEALLOCATE curDatabases;
END;

但是,如果您与 EF o Nhibernate 一起工作,您将处于困境

我发现我可以使用 SQL 句柄字符串获取死锁中每个查询的完整 SQL 文本(SQL 句柄包含在死锁图中/XDL ).

例如,死锁内 XML:

<frame procname="adhoc" line="1" stmtend="368" sqlhandle="0x02000000309a63065dbc0db09405222fe0f66eb954ed1d870000000000000000000000000000000000000000">

将 sqlhandle 传递给 sys.dm_exec_sql_text

 SELECT * FROM sys.dm_exec_sql_text(0x02000000309a63065dbc0db09405222fe0f66eb954ed1d870000000000000000000000000000000000000000000000000000000000000000000000000) -- modify this value with your actual sql_handle