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.
if a membered does not exist in Notes table, then the insert line before should be rolledback. I thought by putting both lines in the Begin transaction, it would rollback, but because no error is thrown, it executes the insert command. How do I throw an error if membered does not exist, so that the whole block of code is rolled back?
begin try
begin transaction
insert into notes (memberid, NoteEffDate, LoginName, NoteDesc) values (367737, GETDATE(), 'marc', 'blah blah')
IF EXISTS (SELECT memberid FROM notes WHERE memberid =4774769)
begin
update notes set notedesc = 'hello there' where memberid = 4774769
end
commit transaction
end try
begin catch
rollback transaction
select ERROR_MESSAGE()
end catch
go
It looks like you're trying to use some version of Sam Saffron's upsert method.
A rough version of what a procedure would look like for that:
create procedure dbo.notes_upsert (
#memberid int
, #notesdesc varchar(256)
, #loginname varchar(256)
) as
begin
set nocount on;
set xact_abort on;
begin tran
update notes (with serializable)
set notedesc = #notedesc
where memberid = #memberid;
if ##rowcount = 0
begin;
insert into notes (memberid, NoteEffDate, LoginName, NoteDesc)
values (#memberid, getdate(), #loginname, #notesdesc);
end;
commit tran
end;
You could RaiseError in your Try Block.
begin try
begin transaction
insert into notes (memberid, NoteEffDate, LoginName, NoteDesc) values (367737, GETDATE(), 'marc', 'blah blah')
IF EXISTS (SELECT memberid FROM notes WHERE memberid =4774769)
begin
update notes set notedesc = 'hello there' where memberid = 4774769
end
ELSE
-- RAISERROR with severity 11-19 will cause execution to
-- jump to the CATCH block.
RAISERROR ('Error raised in TRY block.', -- Message text.
16, -- Severity.
1 -- State.
);
commit transaction
end try
begin catch
rollback transaction
select ERROR_MESSAGE()
end catch
go
I am new to databases and i try to create a stored procedure that inserts data in tables that are in a many to many relation.If any part of the operation fails then it must try to recover as much as possible from the entire operation. For example, if one wants to create a record regarding publishers and books and succeeds creating the publisher but fails with the book, then it should roll back
the creation of the book, but not of the publisher.
My code looks like this:
BEGIN TRY
BEGIN TRANSACTION
DECLARE #serviciuKey int
DECLARE #specializareKey int
IF NOT EXISTS (SELECT denumire, moneda, pret FROM Serviciu where denumire=#denumire and moneda=#moneda and pret=#pret)
BEGIN
INSERT INTO Serviciu ( denumire, moneda, pret)
VALUES (#denumire, #moneda, #pret)
END
SET #serviciuKey=##IDENTITY
SAVE TRANSACTION savepoint
IF NOT EXISTS (SELECT denumire, descriere FROM Specializare where denumire=#denumire_spec AND descriere=#descriere)
BEGIN
INSERT INTO Specializare( denumire, descriere)
VALUES (#denumire_spec, #descriere)
END
SET #specializareKey=##IDENTITY
SAVE TRANSACTION savepoint
IF NOT EXISTS (SELECT * FROM Specializare_Serviciu where cod_specializare=#specializareKey and cod_serviciu=#serviciuKey)
BEGIN
INSERT INTO Specializare_Serviciu( cod_specializare, cod_serviciu)
VALUES (#specializareKey, #serviciuKey)
END
SAVE TRANSACTION savepoint
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF ##trancount > 0 ROLLBACK TRANSACTION savepoint
DECLARE #msg nvarchar(2048) = error_message()
RAISERROR (#msg, 16, 1)
RETURN 55555
END CATCH
When i execute the procedure, i have this error:
Msg 3931, Level 16, State 1, Procedure AddData0, Line 76
The current transaction cannot be committed and cannot be rolled back to a savepoint. Roll back the entire transaction.
Also when i try to insert some data that already exists, it is inserted with another ID, which means that IF NOT EXIST statement is not working.
Any help, please?
Try this version:
DECLARE #serviciuKey int
DECLARE #specializareKey int
BEGIN TRY
BEGIN TRAN
IF NOT EXISTS (SELECT denumire, moneda, pret FROM Serviciu where denumire=#denumire and moneda=#moneda and pret=#pret)
BEGIN
INSERT INTO Serviciu ( denumire, moneda, pret)
VALUES (#denumire, #moneda, #pret)
END
SET #serviciuKey=scope_identity()
IF NOT EXISTS (SELECT denumire, descriere FROM Specializare where denumire=#denumire_spec AND descriere=#descriere)
BEGIN
INSERT INTO Specializare( denumire, descriere)
VALUES (#denumire_spec, #descriere)
END
SET #specializareKey=scope_identity()
IF NOT EXISTS (SELECT * FROM Specializare_Serviciu where cod_specializare=#specializareKey and cod_serviciu=#serviciuKey)
BEGIN
INSERT INTO Specializare_Serviciu( cod_specializare, cod_serviciu)
VALUES (#specializareKey, #serviciuKey)
END
COMMIT TRAN
END TRY
BEGIN CATCH
IF ##trancount > 0
ROLLBACK TRAN
DECLARE #msg nvarchar(2048) = error_message()
RAISERROR (#msg, 16, 1)
RETURN 55555
END CATCH