Related
I'm getting an error message in a stored procedure, saying that I can't insert a NULL value into a table, when I should be getting errors earlier in the code if the value is null.
Here's the relevant part of the stored procedure:
CREATE PROCEDURE [dbo].[udp_AddUpdateStaffVariable]
-- Add the parameters for the stored procedure here
#StaffID int=null,
#VariableTypeID int,
#VariableIntValue int=null,
#VariableVarcharValue varchar(max)=null,
#VariableDatetimeValue datetime=null,
#VariableDecimalValue decimal=null
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
BEGIN TRY
DECLARE #PrintOutput varchar(150)
SET #PrintOutput = '#StaffID = ' + CASE WHEN #StaffID = NULL THEN 'Null' ELSE CONVERT(varchar(20), #StaffID) END
RAISERROR (#PrintOutput, 10, 1) WITH NOWAIT
IF (#StaffID = NULL) -- If the staffid of the current user was not supplied, find it in the Staff table
BEGIN
DECLARE #CurrentUser nvarchar(255) = SUSER_SNAME();
SELECT #StaffID = [StaffID] FROM [dbo].[Staff] WHERE [UserName] = #CurrentUser;
SET #PrintOutput = '#StaffID = ' + CASE WHEN #StaffID = NULL THEN 'Null' ELSE CONVERT(varchar(20), #StaffID) END
RAISERROR (#PrintOutput, 10, 1) WITH NOWAIT
IF #StaffID = NULL -- raise error if staffid wasn't found
BEGIN
RAISERROR (50001 --error number
, 16 -- severity
, 1 --state
, #CurrentUser -- parameter
)
END
END
-- Get the variable data type (used to determine where the variable is stored)
DECLARE #VarDataTypeDesc varchar(20)
DECLARE #StaffVarID int
SELECT #VarDataTypeDesc = dt.[StaffVariableDataType]
FROM [list].[DataTypes] dt INNER JOIN [list].[StaffVariableTypes] svt ON dt.DataTypeID = svt.DataTypeID
WHERE svt.VariableTypeID = #VariableTypeID
-- update or add the staff variable
IF EXISTS (SELECT 1 FROM [dbo].[StaffVariables] WHERE StaffID = #StaffID AND [VariableTypeID] = #VariableTypeID) -- update
BEGIN
IF #VarDataTypeDesc = 'int'
BEGIN -- only update here - other data types are updated further down
UPDATE [dbo].[StaffVariables] SET VariableIntValue = #VariableIntValue WHERE StaffID = #StaffID AND VariableTypeID = #VariableTypeID
END
ELSE -- StaffVariableID is only needed if the variable type is not int
BEGIN
SELECT #StaffVarID = StaffVariableID FROM [dbo].[StaffVariables] WHERE StaffID = #StaffID AND [VariableTypeID] = #VariableTypeID
END
END
ELSE -- insert
BEGIN
IF #VarDataTypeDesc = 'int'
BEGIN
INSERT INTO [dbo].[StaffVariables] (StaffID, VariableTypeID, VariableIntValue)
VALUES (#StaffID, #VariableTypeID, #VariableIntValue)
END
ELSE -- StaffVariableID is only needed if the variable type is not int
BEGIN
DECLARE #StaffVarIDTbl table(ID int)
INSERT INTO [dbo].[StaffVariables] (StaffID, VariableTypeID, VariableIntValue)
OUTPUT INSERTED.[StaffVariableID] INTO #StaffVarIDTbl
VALUES (#StaffID, #VariableTypeID, #VariableIntValue)
SELECT #StaffVarID = ID FROM #StaffVarIDTbl
END
END
-- Cutting out the section where I deal with other variable types besides int here - not relevant to this problem
END TRY
BEGIN CATCH
DECLARE #ErrorMessage NVARCHAR(4000);
DECLARE #ErrorSeverity INT;
DECLARE #ErrorState INT;
SELECT
#ErrorMessage = ERROR_MESSAGE(),
#ErrorSeverity = ERROR_SEVERITY(),
#ErrorState = ERROR_STATE();
-- Use RAISERROR inside the CATCH block to return error
-- information about the original error that caused
-- execution to jump to the CATCH block.
RAISERROR (#ErrorMessage, -- Message text.
#ErrorSeverity, -- Severity.
#ErrorState -- State.
);
END CATCH;
END
Here's the test procedure run code:
DECLARE #return_value int
EXEC #return_value = [dbo].[udp_AddUpdateStaffVariable]
#VariableTypeID = 1,
#VariableIntValue = 10
SELECT 'Return Value' = #return_value
GO
...and here's the response:
Msg 50000, Level 16, State 2, Procedure dbo.udp_AddUpdateStaffVariable, Line 130 [Batch Start Line 2]
Cannot insert the value NULL into column 'StaffID', table 'SnippingDbName.dbo.StaffVariables'; column does not allow nulls. INSERT fails.
(1 row affected)
Completion time: 2020-06-01T21:17:08.2049072-05:00
So... here's the question. The error seems to indicate that it either never ran the whole, if #StaffID = NULL portion of the code, or it did, and didn't find the StaffID and set the #StaffID variable. But if that were the case, why can't I see the results of my earlier RAISERROR statements?
I initially tried PRINT and switched to RAISERROR when PRINT wasn't working.
SQL Server 2017 Developer Edition, SSMS 15.0.18183.0
It was a syntax error, that people commenting on the question figured out. IF (#StaffID = NULL) should have been, IF (#StaffID IS NULL) Fixing that in all places in the procedure fixed the problem, and altering my test Staff record so UserName doesn't match SUSER_SNAME() resulted in the expected error.
I am working on a game database. I have a stored procedure that is executed by the game client in case of teleportation, login, logout, death, etc processes. The game client is hardcoded and is not editable by me.
I am doing stuff in my procedure such as if the character logs in to the game, then add item to the character's inventory.
For each different types of process, I have IF blocks and also I have TRY...CATCH blocks in the each "IF" blocks to be able to handle with any errors in my procedure.
So, my question is that it does make any sense using TRY...CATCH blocks in this way? Or should I use SET XACT_ABORT ON statement instead of TRY...CATCH? Which one is better? By the way, the situation of occurrence of any error in IF block, the block have to be completely ROLLBACK.
Also, my procedure is highly executed by game client. There was almost 800 online character always moving in game and executed my procedure. It should be executed as possible as fast.
ALTER PROCEDURE [dbo].[_AddLogChar]
#CharID INT,
#EventID TINYINT,
#Data1 INT,
#Data2 INT,
#strPos VARCHAR(64),
#Desc VARCHAR(128)
AS
---- !!! KILL PROCEDURE !!! ----
IF (#EventID NOT IN (4,6,20))
BEGIN
RETURN 0;
END
---- BATTLE ARENA | ACADEMY ----
IF (#EventID = 20)
BEGIN
BEGIN TRY
BEGIN TRANSACTION TRAN_Battle_Arena
-- Declaration of variables for battle area conditions
DECLARE #CharInBattle VARCHAR(64) = SUBSTRING(#strPos, 15, 6)
IF (#CharInBattle IN ('0x7edc','0x7edb','0x7ed7','0x7ed3','0x7dd3','0x7ada','0x7ad8','0x7ad7','0x7ad5','0x7ad4','0x79db','0x79da','0x79d8','0x79d7','0x79d5','0x79d4','0x74d6','0x73d7','0x73d6','0x73d5','0x73d4','0x72d7','0x72d6','0x72d5','0x72d4'))
BEGIN
DECLARE #KillingCharname VARCHAR(50) = (SELECT SUBSTRING(#Desc,(PATINDEX('%(%', #Desc)) + 1, ((PATINDEX('%)%', #Desc)) - (PATINDEX('%(%', #Desc))) - 1))
DECLARE #KilledCharname VARCHAR(64) = (SELECT CharName16 FROM SRO_VT_SHARD.._Char WITH (NOLOCK) WHERE CharID = #CharID)
IF((#KillingCharname IS NOT NULL) AND (#KilledCharname IS NOT NULL))
BEGIN
INSERT INTO LOG_BattleHonorRank (KillingCharname, KilledCharname, BattleRegion)
VALUES (#KillingCharname, #KilledCharname, #CharInBattle)
UPDATE SRO_VT_SHARD.._TrainingCamp
SET GraduateCount = (GraduateCount + 1),
EvaluationPoint = EvaluationPoint + 5
WHERE ID = (SELECT CampID FROM SRO_VT_SHARD.._TrainingCampMember WITH (NOLOCK) WHERE CharName = #KillingCharname)
UPDATE SRO_VT_SHARD.._TrainingCamp
SET EvaluationPoint = EvaluationPoint - 6
WHERE ID = (SELECT CampID FROM SRO_VT_SHARD.._TrainingCampMember WITH (NOLOCK) WHERE CharName = #KilledCharname)
END
END
COMMIT TRANSACTION TRAN_Battle_Arena
END TRY
BEGIN CATCH
SELECT
ERROR_LINE() AS ErrorLine,
ERROR_MESSAGE() AS ErrorMessage;
ROLLBACK TRANSACTION TRAN_Battle_Arena
END CATCH
RETURN 1;
END
---- JOB SYSTEM ----
IF(#EventID=6 AND (SELECT [Level] FROM SRO_VT_SHARD.dbo._CharTrijob WHERE CharID=#CharID)=7)
BEGIN
BEGIN TRY
BEGIN TRANSACTION TRAN_Job_System
---------------------------------------------------------------------------------------------------
-- Declaration of variables
---------------------------------------------------------------------------------------------------
DECLARE #Charname16 VARCHAR(64)=(SELECT Charname16 FROM SRO_VT_SHARD.dbo._Char WITH (NOLOCK) WHERE CharID=#CharID)
DECLARE #traderJID INT=(SELECT UserJID FROM SRO_VT_SHARD.dbo._User WITH (NOLOCK) WHERE CharID=#CharID)
DECLARE #SkillID INT
DECLARE #JobBuffLevel INT
---------------------------------------------------------------------------------------------------
-- Check users have any information in SK_Silk or not, if not then begin to addition
---------------------------------------------------------------------------------------------------
IF NOT EXISTS(SELECT JID FROM [SRO_VT_ACCOUNT].[dbo].[SK_Silk] WHERE JID=#traderJID)
BEGIN
INSERT INTO [SRO_VT_ACCOUNT].[dbo].[SK_Silk] (JID, silk_own, silk_gift, silk_point) VALUES(#traderJID, 0, 0, 0);
END
---------------------------------------------------------------------------------------------------
-- Check users have any information in LOG_CharJobStatus or not, if not then begin to addition
---------------------------------------------------------------------------------------------------
IF NOT EXISTS(SELECT CharID FROM SRO_VT_LOG..LOG_CharJobStatus WHERE CharID=#CharID)
BEGIN
INSERT INTO SRO_VT_LOG..LOG_CharJobStatus (CharID, Charname, RestartCount, ObtainedSilk) VALUES(#CharID, #Charname16, 0, 0)
END
---------------------------------------------------------------------------------------------------
-- Begin to add reward silk, restart count, obtained silk & job coins information
---------------------------------------------------------------------------------------------------
UPDATE [SRO_VT_ACCOUNT].[dbo].[SK_Silk] SET silk_own=(silk_own+10) WHERE JID=#traderJID;
UPDATE SRO_VT_LOG..LOG_CharJobStatus SET RestartCount=(RestartCount+1), ObtainedSilk=(ObtainedSilk+10), [Date]=GETDATE() WHERE CharID=#CharID
EXEC SRO_VT_SHARD.dbo._ADD_ITEM_EXTERN #Charname16,'ITEM_ETC_SD_TOKEN_02',4,0
---------------------------------------------------------------------------------------------------
-- Check users restart count modulus, if modulus 10 equals to 0, then begin to add advanced elixir scroll
---------------------------------------------------------------------------------------------------
IF((SELECT (RestartCount % 10) FROM SRO_VT_LOG..LOG_CharJobStatus WHERE CharID=#CharID)=0)
BEGIN
UPDATE SRO_VT_LOG..LOG_CharJobStatus SET Obtained_Advanced_Elixir=(Obtained_Advanced_Elixir+1), [Date]=GETDATE() WHERE CharID=#CharID
EXEC SRO_VT_SHARD.dbo._ADD_ITEM_EXTERN #Charname16,'ITEM_ETC_VENUS_ADVANCED_ELIXIR_SCROLL',1,0
END
---------------------------------------------------------------------------------------------------
-- Check users restart count modulus, if modulus 5 equals to 0, then begin to add job buff
---------------------------------------------------------------------------------------------------
IF((SELECT (RestartCount % 5) FROM SRO_VT_LOG..LOG_CharJobStatus WHERE CharID=#CharID)=0)
BEGIN
IF EXISTS (SELECT JobID FROM SRO_VT_SHARD.._TimedJob WITH (NOLOCK) WHERE CharID=#CharID AND JobID IN (33791,33792,33793,33794,33795,33796,33797,33798,33799,33800))
BEGIN
DELETE FROM SRO_VT_SHARD.._TimedJob WHERE CharID=#CharID AND JobID IN (33791,33792,33793,33794,33795,33796,33797,33798,33799,33800)
END
IF((SELECT BuffLevel FROM SRO_VT_LOG..LOG_CharJobStatus WHERE CharID=#CharID)<=10)
BEGIN
UPDATE SRO_VT_LOG..LOG_CharJobStatus SET BuffLevel=(BuffLevel+1), [Date]=GETDATE() WHERE CharID=#CharID
END
SET #JobBuffLevel=(SELECT BuffLevel FROM SRO_VT_LOG..LOG_CharJobStatus WHERE CharID=#CharID)
SELECT #SkillID=
(CASE
WHEN #JobBuffLevel=1 THEN 33791
WHEN #JobBuffLevel=2 THEN 33792
WHEN #JobBuffLevel=3 THEN 33793
WHEN #JobBuffLevel=4 THEN 33794
WHEN #JobBuffLevel=5 THEN 33795
WHEN #JobBuffLevel=6 THEN 33796
WHEN #JobBuffLevel=7 THEN 33797
WHEN #JobBuffLevel=8 THEN 33798
WHEN #JobBuffLevel=9 THEN 33799
WHEN #JobBuffLevel>=10 THEN 33800
ELSE 0
END)
IF (NOT EXISTS (SELECT JobID FROM SRO_VT_SHARD.._TimedJob WHERE JobID=#SkillID AND CharID=#CharID) AND (#SkillID>0))
BEGIN
INSERT INTO SRO_VT_SHARD.._TimedJob VALUES (#CharID,0,#SkillID,(SELECT DATEDIFF(SECOND,'19700101 00:00:00:000',(SELECT DATEADD(HOUR,72,GETUTCDATE())))),0,1,0,0,0,0,0,0,0,0)
END
END
---------------------------------------------------------------------------------------------------
-- Restart to users job level
---------------------------------------------------------------------------------------------------
UPDATE SRO_VT_SHARD.._CharTrijob SET [Level]=1, [Exp]=0, Contribution=0 WHERE CharID=#CharID
COMMIT TRANSACTION TRAN_Job_System
END TRY
BEGIN CATCH
SELECT
ERROR_LINE() AS ErrorLine,
ERROR_MESSAGE() AS ErrorMessage;
ROLLBACK TRANSACTION TRAN_Job_System
END CATCH
END
----==========================================================================================================----
-------------------------------------------- SILKPERPERIOD -----------------------------------------------
IF(#EventID=4 OR #EventID=6)
BEGIN
BEGIN TRY
BEGIN TRANSACTION TRAN_SilkPerPeriod
---------------------------------------------------------------------------------------------------
-- For login state
---------------------------------------------------------------------------------------------------
IF (#EventID=4)
BEGIN
IF NOT EXISTS(SELECT CharID FROM LOG_CharInOut WHERE CharID=#CharID)
BEGIN
INSERT INTO LOG_CharInOut (CharID,Char_Name,Is_Online,In_Date) VALUES(#CharID, (SELECT CharName16 FROM SRO_VT_SHARD.._Char WITH(NOLOCK) WHERE CharID=#CharID), 1, GETDATE());
END
IF EXISTS(SELECT CharID FROM LOG_CharInOut WHERE CharID=#CharID)
BEGIN
UPDATE LOG_CharInOut SET Is_Online=1, In_Date=GETDATE() WHERE CharID=#CharID
END
END
---------------------------------------------------------------------------------------------------
-- For logout state
---------------------------------------------------------------------------------------------------
IF (#EventID=6)
BEGIN
DECLARE #SilkQuantity INT=1 -- Quantity of silk to be given within the specified period.
DECLARE #ReqTime INT=60 -- The minimum required online period in minutes to be awarded for the silk reward.
UPDATE LOG_CharInOut SET Is_Online=0, Out_Date=GETDATE() WHERE CharID=#CharID
DECLARE #JID INT=(SELECT UserJID FROM SRO_VT_SHARD.dbo._User WITH (NOLOCK) WHERE CharID=#CharID)
DECLARE #LastOnlineTime INT=(SELECT DATEDIFF(MINUTE,(SELECT In_Date FROM LOG_CharInOut WHERE CharID=#CharID),(SELECT Out_Date FROM LOG_CharInOut WHERE CharID=#CharID)))
UPDATE LOG_CharInOut SET Last_OnlineTime=#LastOnlineTime, Total_OnlineTime=Total_OnlineTime+#LastOnlineTime WHERE CharID=#CharID
DECLARE #TotalOnlineTime INT, #UsedOnlineTime INT;
SELECT #TotalOnlineTime=Total_OnlineTime , #UsedOnlineTime=Used_OnlineTime FROM LOG_CharInOut WHERE CharID=#CharID
IF NOT EXISTS(SELECT JID FROM SRO_VT_ACCOUNT..SK_Silk WHERE JID=#JID)
BEGIN
INSERT INTO SRO_VT_ACCOUNT..SK_Silk (JID, silk_own, silk_gift, silk_point) VALUES(#JID, 0, 0, 0);
END
IF EXISTS(SELECT JID FROM SRO_VT_ACCOUNT..SK_Silk WHERE JID=#JID)
BEGIN
IF ((CONVERT(INT,#TotalOnlineTime-#UsedOnlineTime)/#ReqTime)>0)
BEGIN
UPDATE SRO_VT_ACCOUNT..SK_Silk SET silk_point=silk_point+(CONVERT(INT,((#TotalOnlineTime-#UsedOnlineTime)/#ReqTime))*#SilkQuantity) WHERE JID=#JID
UPDATE LOG_CharInOut SET Used_OnlineTime=Used_OnlineTime+((CONVERT(INT,((#TotalOnlineTime-#UsedOnlineTime)/#ReqTime)))*#ReqTime) WHERE CharID=#CharID
END
END
END
COMMIT TRANSACTION TRAN_SilkPerPeriod
END TRY
BEGIN CATCH
SELECT
ERROR_LINE() AS ErrorLine,
ERROR_MESSAGE() AS ErrorMessage;
ROLLBACK TRANSACTION TRAN_SilkPerPeriod
END CATCH
END
----==========================================================================================================----
---------------------------------------------- STAT RESET ------------------------------------------------
IF(#EventID=6 AND EXISTS(SELECT CharID FROM SRO_VT_LOG..LOG_CharStat WHERE CharID=#CharID))
BEGIN
DECLARE #RebirthCountForStat INT=(SELECT RebirthCount FROM SRO_VT_LOG..LOG_CharRebirth WITH (NOLOCK) WHERE CharID=#CharID)
DECLARE #MaxLevel TINYINT=(SELECT MaxLevel FROM SRO_VT_SHARD.._Char WITH (NOLOCK) WHERE CharID=#CharID)
DECLARE #StatPoint SMALLINT, #RemainStatPoint SMALLINT
SET #StatPoint=
(CASE
WHEN #RebirthCountForStat IS NULL THEN #MaxLevel+19
WHEN #RebirthCountForStat <= 5 THEN #MaxLevel+(#RebirthCountForStat*6)+19
WHEN #RebirthCountForStat > 5 THEN #MaxLevel+49
ELSE #MaxLevel+19
END)
SET #RemainStatPoint = (#MaxLevel*3)-3
UPDATE SRO_VT_SHARD.._Char SET Strength=#StatPoint, Intellect=#StatPoint, RemainStatPoint=#RemainStatPoint WHERE CharID=#CharID
DELETE FROM SRO_VT_LOG..LOG_CharStat WHERE CharID=#CharID
END
----==========================================================================================================----
-------------------------------------------- REBIRTH SYSTEM ----------------------------------------------
IF(#EventID=6)
BEGIN
DECLARE #RebirthCount INT=(SELECT RebirthCount FROM SRO_VT_LOG..LOG_CharRebirth WHERE CharID=#CharID)
DECLARE #Is_Active TINYINT=(SELECT Is_Active FROM SRO_VT_LOG..LOG_CharRebirth WHERE CharID=#CharID)
IF(#Is_Active=1 AND #RebirthCount<=5)-- Rebirth Count Limitation-1
BEGIN
UPDATE SRO_VT_SHARD.._Char SET
CurLevel=1,
MaxLevel=1,
ExpOffset=0,
SExpOffset=0,
Strength=20+(#RebirthCount*6),
Intellect=20+(#RebirthCount*6),
RemainSkillPoint=0,
RemainStatPoint=0
WHERE SRO_VT_SHARD.._Char.CharID=#CharID
DELETE CS FROM SRO_VT_SHARD.._RefSkill RS INNER JOIN SRO_VT_SHARD.._CharSkill CS ON CS.CharID=#CharID AND RS.ID=CS.SkillID AND RS.ReqCommon_MasteryLevel1<=110 AND RS.ID NOT IN (1,70,40,2,8421,9354,9355,11162,9944,8419,8420,11526,10625)
UPDATE SRO_VT_SHARD.._CharSkillMastery SET [Level]=0 WHERE CharID=#CharID AND [Level]<=110
UPDATE SRO_VT_LOG..LOG_CharRebirth SET Is_Active=0 WHERE CharID=#CharID
END
END
----==========================================================================================================----
--################################################################################################################```
You should used both. Let's create a simple table to demonstrate why and answer few fundamental questions.
DROP TABLE IF EXISTS [dbo].[StackOverflow];
CREATE TABLE [dbo].[StackOverflow]
(
[StackID] TINYINT
);
Now, execute the following statements (together):
INSERT INTO [dbo].[StackOverflow] ([StackID])
VALUES (104);
INSERT INTO [dbo].[StackOverflow] ([StackID])
VALUES (256);
SELECT [StackID]
FROM [dbo].[StackOverflow];
You will get an error because the second insert is trying to insert value, which can not be stored in TINYINT type.
The ACID transaction has four properties defining it. The first is Atomacity:
An atomic transaction is a set of events that cannot be
separated from one another and must be handled as a single unit of
work.
Knowing the above, one can think that the engine must rollback the two inserts, but it will not. Why?
Because in the context of the SQL Server, there are four methods for controlling transactions:
auto-commit
implicit
explicit
batch-scoped
The default one is auto-commit:
Any single statement that changes data and executes by itself is
automatically an atomic transaction. Whether the change affects one
row or thousands of rows, it must complete successfully for each row
to be committed. You cannot manually rollback an auto-commit
transaction.
As a result - the above two inserts are two separate transactions where the first is committed and second not.
So, let's use implicit transaction applying BEGIN and COMMIT key words to defined the transaction body:
BEGIN TRANSACTION;
INSERT INTO [dbo].[StackOverflow] ([StackID])
VALUES (105);
INSERT INTO [dbo].[StackOverflow] ([StackID])
VALUES (256);
COMMIT TRANSACTION;
SELECT [StackID]
FROM [dbo].[StackOverflow];
So, one can thing that now the engine is going to rollback the two inserts, right? And of course - it will not. Why?
Because, when the XACT_ABORT IS OFF (which is the default):
When SET XACT_ABORT is OFF, in some cases only the Transact-SQL
statement that raised the error is rolled back and the transaction
continues processing.
and when it is ON:
.. if a Transact-SQL statement raises a run-time error, the entire
transaction is terminated and rolled back.
That's what we need and if you try the code below, you can check this:
SET XACT_ABORT ON;
BEGIN TRANSACTION;
INSERT INTO [dbo].[StackOverflow] ([StackID])
VALUES (105);
INSERT INTO [dbo].[StackOverflow] ([StackID])
VALUES (256);
COMMIT TRANSACTION;
SET XACT_ABORT OFF;
SELECT [StackID]
FROM [dbo].[StackOverflow];
So, is this enough? The answer is no - because:
Compile errors, such as syntax errors, are not affected by SET
XACT_ABORT.
Here the first statement is committed, the second - not.
SET XACT_ABORT ON;
BEGIN TRANSACTION;
INSERT INTO [dbo].[StackOverflow] ([StackID])
VALUES (106);
EXECUTE
('
InnnNSERT INTO [dbo].[StackOverflow] ([StackID])
VALUES (256);
');
COMMIT TRANSACTION;
SET XACT_ABORT OFF;
SELECT [StackID]
FROM [dbo].[StackOverflow];
The template I am using when CRUD are performed and I need to rollback some work in case of error is:
SET NOCOUNT, XACT_ABORT ON;
BEGIN TRY;
BEGIN TRANSACTION;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
BEGIN;
ROLLBACK TRANSACTION;
END;
THROW; -- or log error or something else
END CATCH;
SET NOCOUNT, XACT_ABORT OFF;
You can check the Transaction Locking and Row Versioning Guide for more details.
An error occurs while rollback:
Msg 6401, Level 16, State 1, Procedure our_trigger, Line 76
Cannot roll back t1. No transaction or savepoint of that name was found.
And here is the SQL code in the trigger
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [DOCSADM].[our_trigger]
ON [DOCSADM].[PROFILE]
FOR UPDATE, INSERT
AS
DECLARE #DocSystemId AS INTEGER
DECLARE #docNumber AS INTEGER
DECLARE #lastUsedSystemId AS INTEGER
DECLARE #itemType AS VARCHAR(1) --dm10
DECLARE #manualBarcode AS VARCHAR (50)
DECLARE #oldBarcodeFK AS INTEGER
--user typed barcode, it must be inserted/UPDATED into pd_barcode table
DECLARE #COUNTEXISTINGBARCODE AS INTEGER
SET nocount ON;
BEGIN
DECLARE activity_cursor CURSOR local FOR
SELECT
system_id, docnumber,
a_doc_barcode, pd_doc_barcode, item_type
FROM
inserted
--find the last used systemid
SELECT #lastUsedSystemId = lastkey
FROM docsadm.seq_systemkey
OPEN activity_cursor
FETCH next FROM activity_cursor INTO #DocSystemId, #docNumber, #manualBarcode, #oldBarcodeFK, #itemtype
WHILE (##fetch_status <> -1)
BEGIN
IF (( #itemType = 'M' OR #itemType = 'P' ))
--FIND IF IT EXISTS ALREADY A BARCODE
SELECT #COUNTEXISTINGBARCODE = COUNT(*)
FROM docsadm.pd_barcode
WHERE pd_barcode = #manualBarcode
IF (#COUNTEXISTINGBARCODE = 0)-- THERE IS NO EXISTING BARCODE
DECLARE #barcodeSystemId AS INTEGER = 0
BEGIN TRANSACTION t1
BEGIN TRY
-- get next sys id
EXECUTE [DOCSADM].[Sp_nextkey] 'SYSTEMKEY'
SELECT #barcodeSystemId = lastkey
FROM docsadm.seq_systemkey
INSERT INTO docsadm.pd_barcode
VALUES (#manualBarcode, #barcodeSystemId, 'D', NULL, NULL, 'Y', NULL, NULL)
UPDATE docsadm.profile
SET pd_doc_barcode = #barcodeSystemId
WHERE docnumber = #docNumber
COMMIT TRANSACTION t1
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION t1
END CATCH
END
IF (#COUNTEXISTINGBARCODE <> 0)
--YES THERE IS AT LEAST ONE BARCODE
BEGIN
SELECT TOP 1 #barcodeSystemId = system_id
FROM docsadm.pd_barcode
WHERE pd_barcode = #manualBarcode
BEGIN TRANSACTION t1
BEGIN TRY
--update profile's new barcode reference
UPDATE docsadm.profile
SET pd_doc_barcode = #barcodeSystemId
WHERE docnumber = #docNumber
UPDATE docsadm.pd_barcode
SET pd_doc_bcode_used = 'Y'
WHERE system_id = #barcodeSystemId
IF (#oldBarcodeFK <> 0)
BEGIN
--update old barcode as not used!
UPDATE docsadm.pd_barcode
SET pd_doc_bcode_used = 'N'
WHERE system_id = #oldBarcodeFK
END
COMMIT TRANSACTION t1
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION t1
END CATCH
END
FETCH next FROM activity_cursor INTO #DocSystemId, #docNumber, #manualBarcode, #oldBarcodeFK, #itemtype
END
CLOSE activity_cursor
DEALLOCATE activity_cursor
END
The error comes from here
BEGIN CATCH
ROLLBACK TRANSACTION t1
END CATCH
I tried to save trans but i got the same error. I also begin a trans before the loop but the message occured.
This error says you tried to Commit or Rollback a transaction which does not exist. So, Before ROLLBACK check whether there exists an uncommitted transaction. Like this
IF ##TRANCOUNT>0
ROLLBACK TRANSACTION
I am trying to write logs for stored procedures. I am putting my sp in try block and trying to catch exceptions in catch block . If there are any exceptions I am ROLLING BACK the transaction or I am COMMITING the transaction.
USE [RPM_BROKER_TOOL]
GO
/****** Object: StoredProcedure [dbo].[RPM_PROC_UPDATE_BROKER_SETTINGS] Script Date: 09/24/2015 16:28:25 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- AUTHOR : ROHIT NAIK
-- CREATE DATE: 31-JULY-2015
-- DESCRIPTION: Update Broker Tool settings.
-- =============================================
ALTER PROCEDURE [dbo].[RPM_PROC_UPDATE_BROKER_SETTINGS](
#pk_broker_param int,
#companyID int,
--#BrokerSetting xml
#broker_param_id int,
#fixed_param_id int,
#Exchange_Type varchar(10),
#Parameter_Description varchar(100),
#DataSize int,
#DataType varchar(2),
#DataValue varchar(100),
#Dialog_Filter varchar(100),
#ListOfValues varchar(500),
#isMandatory BIT,
#CREATED_IN_MODULE varchar(30),
#CREATE_LOGIN_NAME varchar(10),
#CREATED_ON DATETIME,
#Field_Type varchar(5),
#Updated_Login varchar(10),
#SequenceId int,
#Description varchar(1000)
)
AS
DECLARE #Current_Timestamp DATETIME = current_timestamp,
#RowsInserted int,
#ErrorMessage varchar(1000),
#ErrorTable RPM_EXCEPTION_TYPE_TEMP ,
#ProcName varchar(100)
--#IN_Pk_Broker_Param int used for auditing
DECLARE #ID_MODULE INT,#DATA_VALUE VARCHAR(500)
SET #ID_MODULE = (SELECT ID_MODULE FROM FW_MODULES WHERE CD_MODULE = 'BROKER APP')
SET #ProcName = (SELECT OBJECT_NAME(##PROCID))
BEGIN
BEGIN TRY
BEGIN TRANSACTION
SELECT #DATA_VALUE=DATAVALUE FROM RPM_BROKER_TOOL_CALIBRATION_PARAMETERS WHERE AUDIT_ACTIVE=1 AND FIXED_PARAMETER_ID=#fixed_param_id
IF ISNULL(#DATA_VALUE,'')<>#DataValue
BEGIN
select 1/0
INSERT INTO RPM_BROKER_TOOL_CALIBRATION_PARAMETERS(
ID_COMPANY,
--ID_BROKER_PARAM,
FIXED_PARAMETER_ID,
EXCHANGE_TYPE,
PARAMETER_DESCRIPTION,
DATASIZE,
DATATYPE,
DATAVALUE,
DAILOG_FILE_FILTER,
LOV,
isMandatory,
CREATED_IN_MODULE,
CREATE_LOGIN_NAME,
CREATE_TIME_STAMP,
UPDATED_IN_MODULE,
UPDATE_LOGIN_NAME,
UPDATE_TIME_STAMP,
AUDIT_ACTIVE,
AUDIT_TYPE,
AUDIT_FROM_DATE,
AUDIT_TO_DATE,
SESSION_ID,
Field_Type,
ID_SEQUENCE,
Description
)
VALUES (
#companyID,
--#broker_param_id,
#fixed_param_id,
#Exchange_Type,
#Parameter_Description,
#DataSize,
#DataType,
#DataValue,
#Dialog_Filter,
#ListOfValues,
#isMandatory,
#CREATED_IN_MODULE,
#CREATE_LOGIN_NAME,
#CREATED_ON,
#CREATED_IN_MODULE,
#Updated_Login,
#Current_Timestamp,
1,
'E',
#Current_Timestamp,
'12/31/2099',
null,
#Field_Type,
#SequenceId,
#Description
)
SET #RowsInserted = ##ROWCOUNT
SET #ErrorMessage = 'Number of rows updated: ' + convert(varchar(100),#RowsInserted)
INSERT INTO #ErrorTable (COMPANY_ID,ID_OBJECT,LOGIN_NAME,MODULE_CODE,PROC_NAME,TIME_STAMP,BUSSINESS_MODULE,EXCEPTION_CATEGORY,EXCEPTION_LEVEL,EXCEPTION_DESCRIPTION)
VALUES (#companyID,NULL,#CREATE_LOGIN_NAME,#CREATED_IN_MODULE,#ProcName,#Current_Timestamp,NULL,'Information','Normal',#ErrorMessage)
UPDATE RPM_BROKER_TOOL_CALIBRATION_PARAMETERS
SET AUDIT_ACTIVE = 0, AUDIT_TO_DATE = #Current_Timestamp,
UPDATE_TIME_STAMP = #Current_Timestamp,
UPDATE_LOGIN_NAME = #Updated_Login,
UPDATED_IN_MODULE = #CREATED_IN_MODULE
WHERE PK_BROKER_PARAM = #pk_broker_param
declare #Update_fields varchar(200)
set #Update_fields = #Parameter_Description + ' modified'
EXEC RPM_PROC_INSERT_AUDIT
'4016'
,#companyID
,#pk_broker_param
--,#fixed_param_id
,1
,'E'
,#Parameter_Description
,#Update_fields
,#CREATED_IN_MODULE
,#Updated_Login
,'BROKER APP SETTINGS'
,#CURRENT_TIMESTAMP
END
COMMIT TRANSACTION
END TRY
BEGIN CATCH
SET #ErrorMessage = 'Error : ' + ERROR_MESSAGE() + 'on #Line ' + convert(varchar(1000),ERROR_LINE())
INSERT INTO #ErrorTable (COMPANY_ID,ID_OBJECT,LOGIN_NAME,MODULE_CODE,PROC_NAME,TIME_STAMP,BUSSINESS_MODULE,EXCEPTION_CATEGORY,EXCEPTION_LEVEL,EXCEPTION_DESCRIPTION)
VALUES (#companyID,NULL,#CREATE_LOGIN_NAME,#CREATED_IN_MODULE,#ProcName,#Current_Timestamp,NULL,'Error','High',#ErrorMessage)
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION
END CATCH
EXEC update_db_log #ErrorTable
END
I don't know why you are getting the issue. But when I have done exactly like your code, I got same Transaction Count. Here is my code:
CREATE TABLE
CREATE TABLE [dbo].[Test](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](50) NULL,
[IsActive] [bit] NULL CONSTRAINT [DF_Emails_IsActive] DEFAULT ((1))
)
SQL
BEGIN TRY
BEGIN TRANSACTION
Print(##TRANCOUNT)
Print('start')
Print(##TRANCOUNT)
INSERT INTO Test VALUES ('First')
select 1/0
Print(##TRANCOUNT)
Print('never reach')
INSERT INTO Test VALUES ('Second')
Print(##TRANCOUNT)
COMMIT TRANSACTION
END TRY
BEGIN CATCH
Print(##TRANCOUNT)
Print('catch called')
INSERT INTO Test VALUES ('Third')
Print(##TRANCOUNT)
Print('inserted')
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION
END CATCH
SELECT * FROM Test
And the statement before Rollback is also rolled back. (In your case, INSERT INTO #ErrorTable ... as per my comment. )
Here is the output:
1
start
1
(1 row(s) affected)
(0 row(s) affected)
1
catch called
(1 row(s) affected)
1
inserted
(0 row(s) affected)
When I execute the code :
declare #result int
exec recostcos #result
select #result
I get 'Msg 208, Level 16, State 0, Procedure RecostCOS, Line 138
Invalid object name '#rLB'.'
The strange thing is The first reference to #rLB did not produce any error but the second reference on line 138 produces the error above.
Please find below the code for recostcos :
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
GO
ALTER proc [dbo].[RecostCOS](#result int output)
as
BEGIN TRY
BEGIN TRANSACTION
SET NOCOUNT ON
--variables
declare #itemName varchar(50)
declare #invoiceNo varchar(50)
declare #customerName varchar(100)
declare #recordedBy varchar(100)
declare #transDate datetime
declare #supCode varchar(50)
declare #decemQty1 decimal(18,6)
declare #decemQty2 decimal(18,6)
declare #decemQty3 decimal(18,6)
declare #recostQty decimal(18,6)
declare #count int
declare #innerCount int
declare #outputCode int
declare #outputCounter int
declare #recId int
declare #innerId int
create table #supcodes(supcode varchar(50),item_name varchar(50),
qty decimal(18,6),id int identity(1,1))
create table #recostList(invoice_no varchar(50),qty decimal(18,6), id int identity(1,1))
create table #rLB(invc_no varchar(50),qty decimal(18,6), id int identity(1,1))
--check for supplyCode needing reprocessing
select #count=count(*) from someTable where recosted=0;
if(#count<=0)
begin
commit
set #result=0
return #result
end
--
insert into #supcodes(supcode,item_name,qty)
select supply_code,item_name,quantity from someTable
where recosted=0
--recost each supply code needing recosting
while(#count>0)
begin
select top 1 #supCode=supcode,#itemName=item_name,#decemQty1=qty,#recId=id
from #supcodes
select #decemQty2=sum(current_qty) from someTable2 where item_name=#itemName
select #decemQty3=quantity from someTable3 where item_name=#itemName
if(#decemQty2 is null)
set #decemQty2=0
if(#decemQty3 is null)
set #decemQty3=0
--debug
if(#decemQty2<>#decemQty3)
begin
--check if there is a log of this error
select #count=count(*) from someTable4 where error_code=1 and
item_name=#itemName and supply_code=#supCode and resolved=0
if(#count<=0)
--error in stock quantities (this must be resolved b4 any recosting)
insert into someTable4(supply_code,item_name,error_code,error_source,error_detail)
values(#supCode,#itemName,1,'re-costing','Mismatch in stock quantities in stock state and supply codes tables')
end
else if(#decemQty1>#decemQty2)
begin
--check if there is a log of this error
select #count=count(*) from someTable4 where error_code=2 and
item_name=#itemName and supply_code=#supCode and resolved=0
--insufficient stock for recosting
if(#count<=0)
insert into someTable4(supply_code,item_name,error_code,error_source,error_detail)
values(#supCode,#itemName,2,'re-costing','insufficient stock for recosting')
end
else
begin
--recost cost of sales of item involved
--get list of invoices of item to be recosted
set #recostQty=#decemQty1
insert into #recostList(invoice_no,qty)
select invoice_no,quantity from someTable5
where supply_code=#supCode and item_name=#itemName
insert into #rLB(invc_no,qty) select invoice_no,qty from #recostList
--delete cost of sales relating to supcod and item in stock account
select #innerCount=count(*) from #recostList
while(#innerCount>0)
begin
select top 1 #invoiceNo=invoice_no,#decemQty2=qty,#innerId=id
from #recostList
delete someTable6 where description
like 'sales of '+ltrim(rtrim(convert(varchar(20),#decemQty2)))+'%'+
#itemName+'%'+#invoiceNo
delete someTable5 where supply_code=#supCode and item_name=#itemName
and quantity=#decemQty2
delete #recostList where id=#innerId
set #innerCount=#innerCount-1
end
--call costByFIFO to recost item
select #innerCount=count(*) from #rLB
set #outputCounter=#innerCount
while(#innerCount>0)
begin
select top 1 #invoiceNo=invc_no,#decemQty2=qty,#innerId=id
from #rLB
select #customerName=customer_name, #transDate=trans_date,
#recordedBy=recorded_by from someTable7 where invoice_no=#invoiceNo
exec #outputCode=costByFIFO #itemName,#invoiceNo,#customerName,
#decemQty2,#transDate,#recordedBy
--ensure each invoice is costed or reverse entire process
if(#outputCounter=0)
begin
set #outputCounter=#outputCounter-1
end
else
begin
set #result=3 --failed to cost all invoices involved
rollback
end
delete #rLB where id=#innerId
set #innerCount=#innerCount-1
end
--outputCounter must be 0 to indicate all invoices where costed
if(#outputCounter<>0)
begin
set #result=3 --failed to cost all invoices involved
rollback
end
else
begin
update someTable set recosted=1 where supply_code=#supCode
and item_name=#itemName
end
end
delete #supcodes where id=#recId
set #count=#count-1
end
SET NOCOUNT OFF
drop table #supcodes
drop table #recostList
drop table #rLB
set #result=0
COMMIT
END TRY
BEGIN CATCH
SELECT ERROR_NUMBER() ErrorNBR, ERROR_SEVERITY() Severity,
ERROR_LINE () ErrorLine, ERROR_MESSAGE() Msg
set #result=2 --unexpected error occured
ROLLBACK
END CATCH