Hal*_*mus 2 t-sql sql-server stored-procedures
我正在开发游戏数据库。我有一个存储过程,由游戏客户端在传送、登录、注销、死亡等过程中执行。游戏客户端是硬编码的,我无法编辑。
我正在我的程序中做一些事情,例如如果角色登录到游戏,然后将项目添加到角色的库存中。
对于每种不同类型的流程,我都有IF
块,并且TRY...CATCH
每个“IF”块中都有块,以便能够处理程序中的任何错误。
TRY...CATCH
所以,我的问题是,以这种方式使用块确实有意义吗?或者我应该使用SET XACT_ABORT ON
声明而不是TRY...CATCH
?哪一个更好?顺便说一句,如果IF
块中出现任何错误,则该块必须是完整的ROLLBACK
。
另外,我的程序是由游戏客户端高度执行的。有近 800 个在线角色一直在游戏中移动并执行我的程序。它应该尽可能快地执行。
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
----==========================================================================================================----
--################################################################################################################```
Run Code Online (Sandbox Code Playgroud)
你应该同时使用两者。让我们创建一个简单的表格来演示原因并回答一些基本问题。
DROP TABLE IF EXISTS [dbo].[StackOverflow];
CREATE TABLE [dbo].[StackOverflow]
(
[StackID] TINYINT
);
Run Code Online (Sandbox Code Playgroud)
现在,(一起)执行以下语句:
INSERT INTO [dbo].[StackOverflow] ([StackID])
VALUES (104);
INSERT INTO [dbo].[StackOverflow] ([StackID])
VALUES (256);
SELECT [StackID]
FROM [dbo].[StackOverflow];
Run Code Online (Sandbox Code Playgroud)
您将收到错误,因为第二次插入正在尝试插入无法存储在TINYINT
类型中的值。
ACID 事务有四个定义它的属性。第一个是Atomacity
:
原子事务是一组不能相互分离的事件,必须作为单个工作单元进行处理。
知道了上述内容,我们就可以认为引擎必须回滚这两个插入,但事实并非如此。为什么?
因为在SQL Server的上下文中,有四种控制事务的方法:
默认的是auto-commit
:
任何更改数据并自行执行的单个语句自动成为原子事务。无论更改影响一行还是数千行,它都必须成功完成才能提交每一行。您无法手动回滚自动提交事务。
因此,以上两个插入是两个独立的事务,其中第一个事务已提交,第二个事务未提交。
那么,让我们使用implicit
交易申请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];
Run Code Online (Sandbox Code Playgroud)
那么,我们可以知道现在引擎将回滚两个插入,对吗?当然 -它不会。为什么?
因为,当XACT_ABORT 为 OFF时(这是默认设置):
当 SET XACT_ABORT 为 OFF 时,在某些情况下,仅回滚引发错误的 Transact-SQL 语句,而事务继续处理。
当它是ON
:
.. 如果 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];
Run Code Online (Sandbox Code Playgroud)
那么,这就足够了吗?答案是否定的——因为:
编译错误(例如语法错误)不受 SET XACT_ABORT 的影响。
这里第一个语句已提交,第二个语句则不提交。
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];
Run Code Online (Sandbox Code Playgroud)
执行 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;
Run Code Online (Sandbox Code Playgroud)
您可以查看事务锁定和行版本控制指南以获取更多详细信息。