سؤال

لديّ SP في SQL Server الذي يعمل مئات المرات في الدقيقة ، ويحتاج إلى التحقق من حركة المرور الواردة مقابل قاعدة بيانات. في الوقت الحالي يفعل ما يلي

INSERT INTO table
SELECT @value1,@value2 WHERE NOT EXISTS 
(SELECT * FROM table WHERE value1 = @value1 AND value2 = @value2);

ومع ذلك ، يمكنني أيضا الذهاب مع

IF NOT EXISTS(SELECT * FROM table WHERE value1 = @value1 AND value2 = @value2)    
   INSERT INTO table (value1,value2) VALUES (@value1,@value2);

أيهما سيكون أسرع؟ أشعر أنه لا يوجد فرق كبير بينهما ، لكنني لست جيدًا من الناحية التاريخية في TSQL ... =/

تحديث: يصيح ... يعني ذكر أن الموجود موجود يستخدم أكثر من قيمة واحدة للعثور على سجل ، لذلك لن يعمل القيد الفريد. تم تحرير العينة لتعكس ذلك ...

هل كانت مفيدة؟

المحلول

بعد إضافة تعليقات Gazillion على هذا السؤال وإجاباته ، سأستمر في الإجابة عليه.

لا أتوقع أي اختلاف كبير في الأداء بين الاثنين المقترحين في السؤال الأصلي. من ناحية ، كما أشار راي ، قد يوفر لك النهج الثاني من القيام ببعض الاستعدادات للإدراج ، ولكن من ناحية أخرى ، فإن RDBMs عادة ما يؤدي بشكل أفضل مع عبارات الدُفعات ، كما في الحل الأول.

يقترح KM و DVK إضافة أ UNIQUE القيد ، الذي سيجعل اختبار التفرد ضمنيًا ، ولكن سيتطلب منك إضافة نوع من الأخطاء في التعامل معك INSERT بيان. أواجه صعوبة في اكتشاف سبب إضافة أي أداء إضافي ، على افتراض أن لديك بالفعل فهرس يغطي العمودين. إذا لم يكن لديك مثل هذا الفهرس ، فأضفه ، وأعيد النظر في حاجتك لمزيد من الأداء.

ما إذا كان يتم إجراء فحص التفرد بشكل صريح أو ضمني لا ينبغي أن يهم AFAIK. إذا تم الحصول على أي شيء من خلال إجراء فحص "داخل" معدة DBMS ، فقد يتم تناول هذا الكسب من خلال النفقات العامة المرتبطة برفع الأخطاء والتعامل معها عند وجود التكرارات.


خلاصة القول: على افتراض أن الفهرس موجود بالفعل ، إذا كنت لا تزال تجد نفسك شهية للأداء ، فإن توصيتي هي أن تقوم بإجراء اختبارات تجريبية على الحلول الثلاثة المقترحة. طهي برنامجًا صغيرًا يحاكي بيانات الإدخال المتوقعة ، وتفجير كل حلول من الحلول الثلاثة مع بضعة مليارات الصفوف ، بما في ذلك كمية معقولة من التكرارات. افعل هذا ، تأكد من نشر نتائجك :-)

نصائح أخرى

كلا الاختلافين غير صحيحين. ستقوم بإدراج أزواج من duplicate @value1 ، @value2 ، مضمون.

الطريقة الصحيحة للتعامل مع هذا هي إنفاذ قيود فريدة اثنين الأعمدة وإدراج انتهاك القيد والتعامل معها دائمًا:

ALTER TABLE Table ADD CONSTRAINT uniqueValue1Value UNIQUE (value1, values2);

وإدراج:

BEGIN TRY
   INSERT INTO Table (value1, value2) VALUES (@value1, @value2);
END TRY
BEGIN CATCH
   DECLARE @error_number int, @error_message NVARCHAR(4000), @xact_state INT;
   SET @error_number = ERROR_NUMBER();
   SET @error_message = ERROR_MESSAGE();
   SET @xact_state = XACT_STATE();
   IF (@xact_state = -1)
   BEGIN
     ROLLBACK TRANSACTION;
   END
   IF (@error_number != 2627) /* 2627 is ' Cannot insert duplicate key in object ...' */
   BEGIN
      RAISERROR(N'Error inserting into Table: %i %s', 16,1, @errror_number, @error_message);
   END
ENd CATCH

في حين أن هذه قد تبدو معقدة ، يجب على المرء أن يعامل بتفاصيل صغيرة مسماة صحة. هذا أكثر بساطة بكثير عند مقارنته بحل قائم على تلميحات القفل. هذا هو أيضا الحل الأكثر أداء: لا يسعى واحد فقط. تحتاج جميع الحلول الأخرى إلى تسعى على الأقل (أحدهما للتحقق من أنه يمكن إدخاله ، واحد لإدراجه).

في بيئة بالكاد متزامنة ، متزامنة INSERT يمكن أن يحدث بين بين IF NOT EXISTS و INSERT في استفسارك الثاني.

سيضع استفسارك الأول الأقفال المشتركة في السجل الذي يفحصه ، والذي لن يتم رفعه حتى نهاية الاستعلام ، لذلك سيكون من المستحيل إدراج سجل جديد حتى يتم تشغيل الاستعلام.

ومع ذلك ، يجب ألا تعتمد فقط على هذا السلوك. ضع إضافية UNIQUE قيد على value.

لن تجعل قاعدة البيانات أكثر اتساقًا فحسب ، بل ستنشئ فهرسًا سيجعل الاستعلام الأول أكثر سرعة.

إذا كنت تريد أن تكون القيم فريدة من نوعها ، فلماذا لا تنشئ مجرد قيود فريدة من نوعها على القيمة ، فقم بإدراج دون تحديد خطأ في انتهاك القيد والتعامل معه بأمان؟

سيكون ذلك أسرع من أي من هذه الأساليب.

أيضًا ، لا يعمل النهج الأول لك - بحلول الوقت الذي تحصل فيه على الاختيار ، قمت بالفعل بإدراج القيمة ، لذا فمن الواضح أن SELECT ستجد ما أدخلته للتو.

فقط قم بذلك ، وتجاهل أي خطأ (يفترض قيودًا فريدة من نوعها على القيمة) ...

BEGIN TRY
    INSERT INTO Table (value) VALUES (@value);
END TRY
BEGIN CATCH
    PRINT 'it was already in there!'
END CATCH

حيث هذا يدير مئات المرات في الدقيقة, ، يجب إضافة تلميحات قفل إلى الاختيار والمعاملة إلى تجنب حالة السباق

(SELECT * FROM Table WITH (UPDLOCK, HOLDLOCK)  WHERE value = @value);

ومع ذلك ، فإن فكرتي المقترحة عن إدراج وتجاهل أي خطأ في القيد مكرر من شأنه أن يتجنب حالة السباق أيضًا.

إذا اضطررت إلى تخمين ، أعتقد أن الخيار الثاني سيكون أسرع. لن يتعين على SQL Server القيام بأي نوع من الإعدادات للإدراج إذا فشل موجود ، بينما في الأول ، قد يبحث عن بعض أسماء الجدول والحقل والاستعداد لإدراج لا يحدث أبدًا. ومع ذلك ، سأحاول ذلك في محلل الاستعلام وأرى ما تقوله الخطة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top