Sql server SQL Server在存储过程内的vs TRY…CATCH块上设置XACT_ABORT
我正在制作一个游戏数据库。我有一个存储过程,在传送、登录、注销、死亡等过程中由游戏客户端执行。游戏客户端是硬编码的,我无法编辑 我在我的过程中做一些事情,比如如果角色登录到游戏中,然后将物品添加到角色的库存中 对于每种不同类型的进程,我都有IF块,而且我在每个IF块中都有TRY…CATCH块,以便能够处理过程中的任何错误 所以,我的问题是,以这种方式使用TRY…CATCH块是否有意义?或者我应该在语句上使用SET XACT_ABORT而不是TRY…CATCH?哪一个更好?顺便说一下,如果在IF块中出现任何错误,该块必须完全回滚 此外,我的程序由游戏客户端高度执行。几乎有800个在线角色总是在游戏中移动并执行我的程序。它应该尽可能快地执行Sql server SQL Server在存储过程内的vs TRY…CATCH块上设置XACT_ABORT,sql-server,tsql,stored-procedures,Sql Server,Tsql,Stored Procedures,我正在制作一个游戏数据库。我有一个存储过程,在传送、登录、注销、死亡等过程中由游戏客户端执行。游戏客户端是硬编码的,我无法编辑 我在我的过程中做一些事情,比如如果角色登录到游戏中,然后将物品添加到角色的库存中 对于每种不同类型的进程,我都有IF块,而且我在每个IF块中都有TRY…CATCH块,以便能够处理过程中的任何错误 所以,我的问题是,以这种方式使用TRY…CATCH块是否有意义?或者我应该在语句上使用SET XACT_ABORT而不是TRY…CATCH?哪一个更好?顺便说一下,如果在IF块
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
----==========================================================================================================----
--################################################################################################################```
你应该两者都用。让我们创建一个简单的表格来说明原因并回答几个基本问题
DROP TABLE IF EXISTS [dbo].[StackOverflow];
CREATE TABLE [dbo].[StackOverflow]
(
[StackID] TINYINT
);
现在,一起执行以下语句:
INSERT INTO [dbo].[StackOverflow] ([StackID])
VALUES (104);
INSERT INTO [dbo].[StackOverflow] ([StackID])
VALUES (256);
SELECT [StackID]
FROM [dbo].[StackOverflow];
您将得到一个错误,因为第二次插入尝试插入值,该值不能存储在TINYINT类型中
ACID事务有四个属性定义它。第一个是原子性:
原子事务是一组不可更改的事件
相互分离,必须作为一个单独的设备进行处理
工作
了解以上内容后,可以认为引擎必须回滚这两个插入,但不会。为什么?
因为在SQL Server的上下文中,有四种控制事务的方法:
自动提交
含蓄的
明确的
批量范围
默认设置为自动提交:
任何更改数据并自行执行的语句都是
自动生成原子事务。变化是否影响一个人
行或数千行,每行必须成功完成
承诺。无法手动回滚自动提交
交易
因此,上述两个插入是两个独立的事务,其中第一个已提交,第二个未提交
因此,让我们使用隐式事务应用BEGIN和COMMIT关键字来定义事务体:
BEGIN TRANSACTION;
INSERT INTO [dbo].[StackOverflow] ([StackID])
VALUES (105);
INSERT INTO [dbo].[StackOverflow] ([StackID])
VALUES (256);
COMMIT TRANSACTION;
SELECT [StackID]
FROM [dbo].[StackOverflow];
所以,有一件事是,现在引擎将回滚两个插入,对吗?当然,它不会。为什么?
因为,当默认值为:
当SET XACT_ABORT处于禁用状态时,在某些情况下,只有Transact-SQL
引发错误的语句将回滚,并且事务
继续处理
当它打开时:
。。如果Transact-SQL语句引发运行时错误,则整个
事务被终止并回滚
这就是我们需要的,如果您尝试下面的代码,您可以检查:
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];
那么,这足够了吗?答案是否定的——因为:
编译错误(如语法错误)不受集合的影响
XACT_中止
这里第一个语句已提交,第二个语句未提交
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];
我在执行CRUD时使用的模板是,如果出现错误,我需要回滚一些工作:
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;
您可以查看以了解更多详细信息。这一存储过程中有太多内容,首先,我将此代码分成三个单独的存储过程来处理事件ID 4、6和20,然后有一个TRY块,根据EventID值调用三个存储过程中的一个,一个CATCH块回滚TRY块中调用的存储过程。最佳实践是在存储过程中执行一件事。代码更易于管理,如果需要,可以很容易地添加更多功能。@M.Ali如何设置xact_abort on vs try..catch?始终尝试catch。必须由Erland Sommarskog读取。你最好同时使用SET XACT_ABORT和TRY CATCH。我同意@VladimirBaranov的观点。XACT_ABORT ON的好处也在于,在查询超时的情况下,它会立即回滚事务,此时不会执行CATCH块。因此,我可以使用begin和commit事务之间的所有IF块吗?您共享了上一个模板?这会导致性能问题吗?此外,一些IF块包含返回语句。模板中是否仍然需要它?通常最好只在begin commit块中保留CRUD操作。准备部分可以介于begin try和begin事务之间-例如,您可以读取一些数据、修改数据、聚合数据,然后仅将插入部分放入事务块中。您可以安全地使用上面包装在IF中的模板——当然,设置的部分可以只保留在beg和存储过程的末尾。这里,Max Vernon在回答中说,有可能出现回滚在CATCH中不起作用的情况,因此检查XACT_状态i是一种明智的做法 n此处的捕获块示例C:。