حفظ المعاملة مقابل بدء المعاملة (SQL Server) كيفية تداخل المعاملات بشكل جيد
-
14-12-2019 - |
سؤال
لدي إجراء مخزن يحتاج إلى تعيين نقطة حفظ حتى يتمكن، في ظل ظروف معينة، من التراجع عن كل ما فعله وإعادة رمز خطأ إلى المتصل، أو قبوله/الالتزام به وإعادة النجاح إلى المتصل.لكنني أحتاجه للعمل سواء كان المتصل قد بدأ بالفعل معاملة أم لا.المستند مربك للغاية حول هذا الموضوع.هذا ما أعتقد أنه سينجح، لكنني لست متأكدًا من جميع العواقب.
الشيء هو - هذا Stored Procedure (SP)
يسمى من قبل الآخرين.لذلك لا أعرف إذا كانوا قد بدأوا الصفقة أم لا ...حتى لو طلبت من المستخدمين بدء معاملة لاستخدام SP الخاص بي، فلا يزال لدي أسئلة حول الاستخدام السليم لـ Save Points
...
سيختبر مقدم الخدمة الخاص بي ما إذا كانت المعاملة قيد التقدم، وإذا لم تكن كذلك، فابدأ بها BEGIN TRANSACTION
.إذا كانت المعاملة جارية بالفعل، فسيتم بدلاً من ذلك إنشاء نقطة حفظ بها SAVE TRANSACTION MySavePointName
, وحفظ الحقيقة هذا ما فعلته.
ثم إذا اضطررت إلى التراجع عن التغييرات التي أجريتها، إذا قمت بذلك BEGIN TRANSACTION
في وقت سابق، ثم سأفعل ROLLBACK TRANSACTION
.إذا قمت بنقطة الحفظ، فسوف أفعل ذلك ROLLBACK TRANSACTION MySavePointName
.يبدو أن هذا السيناريو يعمل بشكل رائع.
هذا هو المكان الذي أشعر فيه بالارتباك قليلاً - إذا كنت أرغب في الاحتفاظ بالعمل الذي قمت به، وإذا بدأت معاملة فسوف أقوم بتنفيذها COMMIT TRANSACTION
.ولكن إذا قمت بإنشاء نقطة حفظ؟حاولت COMMIT TRANSACTION MySavePointName
, ، ولكن بعد ذلك يحاول المتصل تنفيذ معاملته ويحصل على خطأ:
لا يحتوي طلب "إجراء المعاملة" على "بدء المعاملة" المقابلة.
لذلك أتساءل إذن - يمكن التراجع عن نقطة الحفظ (وهذا يعمل: ROLLBACK TRANSACTION MySavePointName
لن يتم التراجع عن معاملة المتصل).ولكن ربما لا يحتاج المرء أبدًا إلى "ارتكاب" ذلك؟إنها تظل هناك، في حالة احتياجك إلى العودة إليها، ولكنها تختفي بمجرد الالتزام بالمعاملة الأصلية (أو التراجع عنها)؟
إذا كانت هناك طريقة "أفضل" "لدمج" المعاملة، فيرجى إلقاء بعض الضوء عليها أيضًا.لم أكتشف كيفية التعشيش مع BEGIN TRANSACTION
ولكن فقط التراجع عن معاملتي الداخلية أو الالتزام بها.يبدو ROLLBACK
سوف يعود دائمًا إلى المعاملة العليا، بينما COMMIT
يتناقص ببساطة @@trancount
.
المحلول
أعتقد أنني فهمت كل هذا الآن، لذا سأجيب على سؤالي بنفسي...
لقد قمت بتدوين النتائج التي توصلت إليها إذا كنت تريد المزيد من التفاصيل على http://geekswithblogs.net/bbiales/archive/2012/03/15/how-to-nest-transactions-nicely---quotbegin-transactionquot-vs-quotsave.aspx
لذلك يبدأ SP الخاص بي بشيء من هذا القبيل، لبدء معاملة جديدة إذا لم يكن هناك أي معاملة، ولكن استخدم نقطة الحفظ إذا كانت قيد التقدم بالفعل:
DECLARE @startingTranCount int
SET @startingTranCount = @@TRANCOUNT
IF @startingTranCount > 0
SAVE TRANSACTION mySavePointName
ELSE
BEGIN TRANSACTION
-- …
بعد ذلك، عندما تكون مستعدًا لتنفيذ التغييرات، لن تحتاج إلى الالتزام إلا إذا بدأنا المعاملة بأنفسنا:
IF @startingTranCount = 0
COMMIT TRANSACTION
وأخيرًا، للتراجع عن التغييرات التي أجريتها حتى الآن:
-- Roll back changes...
IF @startingTranCount > 0
ROLLBACK TRANSACTION MySavePointName
ELSE
ROLLBACK TRANSACTION
نصائح أخرى
تمتد إجابة بريان ب.
وهذا يضمن أن اسم نقطة الحفظ فريد ويستخدم ميزات TRY/CATCH/THROW الجديدة في SQL Server 2012.
DECLARE @mark CHAR(32) = replace(newid(), '-', '');
DECLARE @trans INT = @@TRANCOUNT;
IF @trans = 0
BEGIN TRANSACTION @mark;
ELSE
SAVE TRANSACTION @mark;
BEGIN TRY
-- do work here
IF @trans = 0
COMMIT TRANSACTION @mark;
END TRY
BEGIN CATCH
IF xact_state() = 1 OR (@trans = 0 AND xact_state() <> 0) ROLLBACK TRANSACTION @mark;
THROW;
END CATCH
لقد استخدمت هذا النوع من مدير المعاملات في الإجراءات المخزنة الخاصة بي:
CREATE PROCEDURE Ardi_Sample_Test
@InputCandidateID INT
AS
DECLARE @TranCounter INT;
SET @TranCounter = @@TRANCOUNT;
IF @TranCounter > 0
SAVE TRANSACTION ProcedureSave;
ELSE
BEGIN TRANSACTION;
BEGIN TRY
/*
<Your Code>
*/
IF @TranCounter = 0
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @TranCounter = 0
ROLLBACK TRANSACTION;
ELSE
IF XACT_STATE() <> -1
ROLLBACK TRANSACTION ProcedureSave;
DECLARE @ErrorMessage NVARCHAR(4000);
DECLARE @ErrorSeverity INT;
DECLARE @ErrorState INT;
SELECT @ErrorMessage = ERROR_MESSAGE();
SELECT @ErrorSeverity = ERROR_SEVERITY();
SELECT @ErrorState = ERROR_STATE();
RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState);
END CATCH
GO