- What causes the deadlock?
I take “Generate SubAccountId” as example.
<1> At last I find “ A TRIGGER tg_s_SAid_Add ON s_SAid FOR INSERT delay to commit transaction” leads to deadlock
<2>Detailed script about tg_s_SAid_Add as following
--use SeedData
--exec sp_helptext tg_s_SAid_Add
CREATE TRIGGER tg_s_SAid_Add ON s_SAid
FOR INSERT
AS
DELETE FROM dbo.s_SAid
- How to track this trigger
<1> Open two SQL Server Query to execute the following script at the same time.
--SELECT GETDATE()
--WAITFOR TIME '20:21:20'
USE SeedData
DECLARE @count INT
SET @count=1
WHILE(@count<1000)
BEGIN
SET @count=@count+1
BEGIN TRY
------------------------------------------Main Process--------------------------------------------------
DECLARE @p_Type CHAR(2)
DECLARE @p_CountryId CHAR(3)
DECLARE @r_SecId CHAR(10)
SET @p_Type='SA'
SET @p_CountryId='USA'
-- Variable declaration
-- Always set error handling variables first
DECLARE @l_Err INTEGER,
@l_Msg NVARCHAR(100),
@l_Id NVARCHAR(15),
@l_ProcName VARCHAR(30),
@l_ProcDB VARCHAR(30)
DECLARE @l_TableName CHAR(6),
@l_Cmd NVARCHAR(100),
@l_OldId INTEGER,
@l_NewId VARCHAR(5)
SET NOCOUNT ON
-- Initialize error handle-related constants
SET @l_Id = ISNULL(@p_Type + @p_CountryId,'')
SET @l_ProcName = OBJECT_NAME (@@PROCID)
SET @l_ProcDB = DB_NAME()
SET @p_Type = RTRIM(LTRIM(@p_Type))
SET @p_CountryId = RTRIM(LTRIM(@p_CountryId))
SET @r_SecId = NULL
IF @p_Type NOT IN ('F0', 'FO', 'FC', 'FE', 'FM', 'FV', 'FS', 'FX', 'E0', 'VP', 'VA', 'B0', 'C0',
'BP', 'BM', 'XI', 'SA', 'SC', 'SP', 'CF', 'FH', 'ST','FA')
BEGIN
SET @l_Err = 50001
SET @l_Msg = 'Invalid input: p_Type.'
GOTO OnError
END
IF LEN(@p_CountryId) <> 3
BEGIN
SET @l_Err = 50001
SET @l_Msg = 'Invalid input: p_CountryId.'
GOTO OnError
END
IF @p_CountryId = '---'
BEGIN
SET @p_CountryId = '000'
END
SELECT @l_TableName = 's_' + @p_Type + 'id'
BEGIN TRANSACTION
SET @l_Cmd = N'INSERT INTO '+@l_TableName+' DEFAULT VALUES'
EXECUTE sp_executesql @l_Cmd
SELECT @l_OldId = @@IDENTITY,
@l_Err = @@ERROR
IF @l_Err <> 0
BEGIN
ROLLBACK TRANSACTION
SET @l_Msg = 'Insert on ' + @l_TableName + ' failed.'
GOTO OnError
END
IF @l_OldId > 0 AND @l_OldId <= POWER(36,4)-1
BEGIN
SET @l_NewId = ResourceData.dbo.fn_chgDecIntToNBaseInt(36, @l_OldId)
SET @l_Err = @@ERROR
IF @l_Err <> 0
BEGIN
ROLLBACK TRANSACTION
SET @l_Msg = 'Call on fn_chgDecIntToNBaseInt failed.'
GOTO OnError
END
IF @l_NewId = NULL
BEGIN
ROLLBACK TRANSACTION
SET @l_Err = 50002
SET @l_Msg = 'Invalid Id output from fn_chgDecIntToNBaseInt.'
GOTO OnError
END
SELECT @l_NewId = RIGHT('00000'+@l_NewId,5)
SELECT @r_SecId = @p_Type + @p_CountryId + @l_NewId
END
ELSE
BEGIN
ROLLBACK TRANSACTION
SET @l_Err = 50002
SET @l_Msg = 'Invalid Id output from insert on ' + @l_TableName
GOTO OnError
END
COMMIT TRANSACTION
--RETURN @@ERROR
-- Error handling section
OnError:
-- Store error and raise error
--EXECUTE sp_LogError @l_Err, @l_Msg, @l_Id, @l_ProcName, @l_ProcDB
--RAISERROR(@l_Msg, 18,1)
--RETURN @l_Err
---------------------------------------------------Main Process------------------------------------------------------
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
DECLARE @ErrorMessage VARCHAR(8000);
SELECT @ErrorMessage = 'ErrorMessage: '+ISNULL(CAST(ERROR_MESSAGE() AS VARCHAR(500)),'-')+CHAR(10)+
'ERROR_SEVERITY: '+ISNULL(CAST(ERROR_SEVERITY()AS VARCHAR(500)),'-')+CHAR(10)+
'ERROR_NUMBER: '+ISNULL(CAST(ERROR_NUMBER()AS VARCHAR(500)),'-')+CHAR(10)+
'ERROR_STATE: '+ISNULL(CAST(ERROR_STATE()AS VARCHAR(500)),'-')+CHAR(10)+
'ERROR_PROCEDURE: '+ISNULL(CAST(ERROR_PROCEDURE()AS VARCHAR(500)),'-')+CHAR(10)+
'ERROR_LINE: '+ISNULL(CAST(ERROR_LINE()AS VARCHAR(500)),'-')
--if deadlock happened ,print the detailed error message
IF ERROR_NUMBER()=1205
BEGIN
PRINT @ErrorMessage
BREAK;
END
END CATCH
END
<2> The print error message as following
ErrorMessage: Transaction (Process ID 51) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
ERROR_SEVERITY: 13
ERROR_NUMBER: 1205
ERROR_STATE: 45
ERROR_PROCEDURE: tg_s_SAid_Add
ERROR_LINE: 4
<3> We can know code line 4 of trigger “tg_s_SAid_Add” as the red font leads to the deadlock cregarding the error message
CREATE TRIGGER tg_s_SAid_Add ON s_SAid
FOR INSERT
AS
DELETE FROM dbo.s_SAid
<4>we can use a simple example instead of trigger “tg_s_SAid_Add” as following to do a text
- Create a text table
CREATE TABLE IdText(
[Id] [int] IDENTITY(1,1) NOT NULL)
- b. Open two SQL Server Query to execute the following script at the same time then the same deadlock happened again.
Begin Tran
INSERT INTO IdTable DEFAULT VALUES
WaitFor Delay '00:01:00';
DELETE FROM IdTable
Rollback Tran;
- A solution to avoid deadlock for above simple example is to seperated the big transaction as following and
open two SQL Server Query to execute the following script at the same time then the deadlock will never happened
INSERT INTO IdTable DEFAULT VALUES
DELETE FROM IdTable
- The solution for u_getCode
<1>drop TRIGGER of “tg_s_SAid_Add” first.
<2>Execute delete sql script after transaction commit as Yellow highlight
<3>After drop the TRIGGER then open two SQL Server Query to execute the following script
--SELECT GETDATE()
--WAITFOR TIME '20:21:20'
USE SeedData
DECLARE @count INT
SET @count=1
WHILE(@count<1000)
BEGIN
SET @count=@count+1
BEGIN TRY
------------------------------------------Main Process--------------------------------------------------
DECLARE @p_Type CHAR(2)
DECLARE @p_CountryId CHAR(3)
DECLARE @r_SecId CHAR(10)
SET @p_Type='SA'
SET @p_CountryId='USA'
-- Variable declaration
-- Always set error handling variables first
DECLARE @l_Err INTEGER,
@l_Msg NVARCHAR(100),
@l_Id NVARCHAR(15),
@l_ProcName VARCHAR(30),
@l_ProcDB VARCHAR(30)
DECLARE @l_TableName CHAR(6),
@l_Cmd NVARCHAR(100),
@l_OldId INTEGER,
@l_NewId VARCHAR(5)
SET NOCOUNT ON
-- Initialize error handle-related constants
SET @l_Id = ISNULL(@p_Type + @p_CountryId,'')
SET @l_ProcName = OBJECT_NAME (@@PROCID)
SET @l_ProcDB = DB_NAME()
SET @p_Type = RTRIM(LTRIM(@p_Type))
SET @p_CountryId = RTRIM(LTRIM(@p_CountryId))
SET @r_SecId = NULL
IF @p_Type NOT IN ('F0', 'FO', 'FC', 'FE', 'FM', 'FV', 'FS', 'FX', 'E0', 'VP', 'VA', 'B0', 'C0',
'BP', 'BM', 'XI', 'SA', 'SC', 'SP', 'CF', 'FH', 'ST','FA')
BEGIN
SET @l_Err = 50001
SET @l_Msg = 'Invalid input: p_Type.'
GOTO OnError
END
IF LEN(@p_CountryId) <> 3
BEGIN
SET @l_Err = 50001
SET @l_Msg = 'Invalid input: p_CountryId.'
GOTO OnError
END
IF @p_CountryId = '---'
BEGIN
SET @p_CountryId = '000'
END
SELECT @l_TableName = 's_' + @p_Type + 'id'
BEGIN TRANSACTION
SET @l_Cmd = N'INSERT INTO '+@l_TableName+' DEFAULT VALUES'
EXECUTE sp_executesql @l_Cmd
SELECT @l_OldId = @@IDENTITY,
@l_Err = @@ERROR
IF @l_Err <> 0
BEGIN
ROLLBACK TRANSACTION
SET @l_Msg = 'Insert on ' + @l_TableName + ' failed.'
GOTO OnError
END
IF @l_OldId > 0 AND @l_OldId <= POWER(36,4)-1
BEGIN
SET @l_NewId = ResourceData.dbo.fn_chgDecIntToNBaseInt(36, @l_OldId)
SET @l_Err = @@ERROR
IF @l_Err <> 0
BEGIN
ROLLBACK TRANSACTION
SET @l_Msg = 'Call on fn_chgDecIntToNBaseInt failed.'
GOTO OnError
END
IF @l_NewId = NULL
BEGIN
ROLLBACK TRANSACTION
SET @l_Err = 50002
SET @l_Msg = 'Invalid Id output from fn_chgDecIntToNBaseInt.'
GOTO OnError
END
SELECT @l_NewId = RIGHT('00000'+@l_NewId,5)
SELECT @r_SecId = @p_Type + @p_CountryId + @l_NewId
END
ELSE
BEGIN
ROLLBACK TRANSACTION
SET @l_Err = 50002
SET @l_Msg = 'Invalid Id output from insert on ' + @l_TableName
GOTO OnError
END
COMMIT TRANSACTION
--here ,execute delete sql script
SET @l_Cmd = N'DELETE FROM '+@l_TableName
EXECUTE sp_executesql @l_Cmd
--RETURN @@ERROR
-- Error handling section
OnError:
-- Store error and raise error
--EXECUTE sp_LogError @l_Err, @l_Msg, @l_Id, @l_ProcName, @l_ProcDB
--RAISERROR(@l_Msg, 18,1)
--RETURN @l_Err
---------------------------------------------------Main Process------------------------------------------------------
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
DECLARE @ErrorMessage VARCHAR(8000);
SELECT @ErrorMessage = 'ErrorMessage: '+ISNULL(CAST(ERROR_MESSAGE() AS VARCHAR(500)),'-')+CHAR(10)+
'ERROR_SEVERITY: '+ISNULL(CAST(ERROR_SEVERITY()AS VARCHAR(500)),'-')+CHAR(10)+
'ERROR_NUMBER: '+ISNULL(CAST(ERROR_NUMBER()AS VARCHAR(500)),'-')+CHAR(10)+
'ERROR_STATE: '+ISNULL(CAST(ERROR_STATE()AS VARCHAR(500)),'-')+CHAR(10)+
'ERROR_PROCEDURE: '+ISNULL(CAST(ERROR_PROCEDURE()AS VARCHAR(500)),'-')+CHAR(10)+
'ERROR_LINE: '+ISNULL(CAST(ERROR_LINE()AS VARCHAR(500)),'-')
--if deadlock happened ,print the detailed error message
IF ERROR_NUMBER()=1205
BEGIN
PRINT @ErrorMessage
BREAK;
END
END CATCH
END
<4>Text more times find everything is ok