it-swarm.dev

كيفية تجنب الخطأ "القسمة على الصفر" في SQL؟

لدي رسالة الخطأ هذه:

رسالة 8134 ، المستوى 16 ، الحالة 1 ، السطر 1 ، قسّم على صفر خطأ صادف.

ما هي أفضل طريقة لكتابة كود SQL حتى لا أرى رسالة الخطأ هذه مرة أخرى؟

يمكنني القيام بأي مما يلي:

  • أضف جملة حيث بحيث لا يكون المقسوم صفرًا

أو

  • يمكنني إضافة بيان حالة ، بحيث يكون هناك علاج خاص للصفر.

هل هي أفضل طريقة لاستخدام جملة NULLIF؟

هل هناك طريقة أفضل ، أو كيف يمكن فرض ذلك؟

306
Henrik Staun Poulsen

لتجنب حدوث خطأ "القسمة على صفر" ، قمنا ببرمجته على النحو التالي:

Select Case when divisor=0 then null
Else dividend / divisor
End ,,,

ولكن هنا طريقة أجمل بكثير للقيام بذلك:

Select dividend / NULLIF(divisor, 0) ...

الآن المشكلة الوحيدة هي تذكر بت NullIf ، إذا كنت تستخدم المفتاح "/".

553
Henrik Staun Poulsen

في حالة رغبتك في إرجاع صفر ، في حالة حدوث تقسيم صفري ، يمكنك استخدام:

SELECT COALESCE(dividend / NULLIF(divisor,0), 0) FROM sometable

لكل مقسوم صفر ، ستحصل على صفر في مجموعة النتائج.

157
Tobias Domhan

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

لنفترض أنك تريد حساب النسب بين الذكور والإناث لمختلف الأندية المدرسية ، ولكنك تكتشف أن الاستعلام التالي يفشل ويصدر خطأ القسمة على الصفر عندما يحاول حساب نسبة نادي Lord of the Rings Club ، الذي لا يوجد لديه نساء :

SELECT club_id, males, females, males/females AS ratio
  FROM school_clubs;

يمكنك استخدام الدالة NULLIF لتجنب القسمة على صفر. NULLIF يقارن تعبيرين ويعودان فارغان إذا كانا متساويين أو التعبير الأول على خلاف ذلك.

أعد كتابة الاستعلام كـ:

SELECT club_id, males, females, males/NULLIF(females, 0) AS ratio
  FROM school_clubs;

أي رقم مقسوم على NULL يعطي NULL ، ولا يتم إنشاء أي خطأ.

59
frank

يمكنك أيضًا القيام بذلك في بداية الاستعلام:

SET ARITHABORT OFF 
SET ANSI_WARNINGS OFF

لذلك إذا كان لديك شيء مثل 100/0 فسيتم إرجاع NULL. لقد قمت بذلك فقط للاستعلامات البسيطة ، لذلك لا أعرف كيف سيؤثر ذلك على الاستعلامات الأطول/المعقدة.

39
Taz

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

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

لذا يسأل إدواردو في التعليقات "ماذا لو وضع المستخدم في 0؟" ، ويدعو أنه ينبغي أن يكون بخير الحصول على 0 في المقابل. إذا كان المستخدم يضع صفرًا في المبلغ ، وتريد إرجاع 0 عند القيام بذلك ، فعليك وضع رمز في مستوى قواعد العمل لالتقاط هذه القيمة وإرجاع 0 ... ليس لديك حالة خاصة حيث القسمة على 0 = 0.

هذا فرق بسيط ، لكنه مهم ... لأنه في المرة القادمة التي يقوم فيها شخص ما باستدعاء وظيفتك ويتوقع منها أن تفعل الشيء الصحيح ، وتؤدي شيئًا غير تقليدي غير صحيح من الناحية الرياضية ، لكن فقط تعامل مع حالة Edge المحددة ، فرصة جيدة لدغ شخص ما في وقت لاحق. أنت لا تنقسم فعليًا على 0 ... أنت فقط تعيد إجابة سيئة على سؤال سيء.

تخيل أنني أشير إلى شيء ما ، وأنا أعلقه. يجب أن أقرأ في قيمة قياس قياس الإشعاع ، لكن في حالة حافة غريبة لم أكن أتوقعها ، قرأت في 0. ثم أسقط القيمة في وظيفتك ... أعدني 0! يا هلا ، لا إشعاع! إلا أنه موجود حقًا وهناك فقط كنت أقدر قيمة سيئة ... لكن ليس لدي أي فكرة. أريد أن يرمي القسمة الخطأ لأنه العلم بأن هناك خطأ ما.

31
Beska

يمكنك على الأقل إيقاف الاستعلام عن طريق الخطأ مع إرجاع NULL إذا كان هناك قسمة على صفر:

SELECT a / NULLIF(b, 0) FROM t 

ومع ذلك ، أودلن/ تحويل هذا إلى Zero مع coalesce كما هو مبين في الإجابة الأخرى التي حصلت على العديد من الأصوات. هذا خطأ تمامًا بالمعنى الرياضي ، وهو أمر خطير نظرًا لأن تطبيقك سيعود على الأرجح بنتائج خاطئة ومضللة.

31
SQLGeorge
SELECT Dividend / ISNULL(NULLIF(Divisor,0), 1) AS Result from table

عن طريق التقاط الصفر مع nullif () ، ثم القيمة الناتجة الناتجة مع isnull () يمكنك التحايل على القسمة على صفر.

21
Gurufaction

يُعد استبدال "divide by zero" بصفر أمرًا مثيرًا للجدل - لكنه أيضًا ليس الخيار الوحيد. في بعض الحالات ، يكون استبدال الرقم 1 مناسبًا (معقولًا). كثيرا ما أجد نفسي أستخدمه

 ISNULL(Numerator/NULLIF(Divisor,0),1)

عندما أبحث في التحولات في الدرجات/التهم ، وأريد التقصير إلى 1 إذا لم يكن لدي بيانات. فمثلا

NewScore = OldScore *  ISNULL(NewSampleScore/NULLIF(OldSampleScore,0),1) 

في كثير من الأحيان ، لقد قمت بالفعل بحساب هذه النسبة في مكان آخر (ليس أقلها لأنه يمكن أن يلقي بعض عوامل الضبط الكبيرة جدًا على القواسم المنخفضة. في هذه الحالة ، عادةً ما أتحكم في OldSampleScore أكبر من العتبة ؛ مما يحول دون الصفر لكن في بعض الأحيان يكون "الاختراق" مناسبًا.

8
N Mason

لقد كتبت وظيفة من حين لآخر للتعامل معها من أجل الإجراءات المخزنة :

print 'Creating safeDivide Stored Proc ...'
go

if exists (select * from dbo.sysobjects where  name = 'safeDivide') drop function safeDivide;
go

create function dbo.safeDivide( @Numerator decimal(38,19), @divisor decimal(39,19))
   returns decimal(38,19)
begin
 -- **************************************************************************
 --  Procedure: safeDivide()
 --     Author: Ron Savage, Central, ex: 1282
 --       Date: 06/22/2004
 --
 --  Description:
 --  This function divides the first argument by the second argument after
 --  checking for NULL or 0 divisors to avoid "divide by zero" errors.
 -- Change History:
 --
 -- Date        Init. Description
 -- 05/14/2009  RS    Updated to handle really freaking big numbers, just in
 --                   case. :-)
 -- 05/14/2009  RS    Updated to handle negative divisors.
 -- **************************************************************************
   declare @p_product    decimal(38,19);

   select @p_product = null;

   if ( @divisor is not null and @divisor <> 0 and @Numerator is not null )
      select @p_product = @Numerator / @divisor;

   return(@p_product)
end
go
5
Ron Savage
  1. أضف قيد CHECK الذي يفرض Divisor ليكون غير صفري
  2. أضف أداة تحقق إلى النموذج حتى لا يتمكن المستخدم من إدخال قيم صفرية في هذا الحقل.
3
finnw

لتحديث SQLs:

update Table1 set Col1 = Col2 / ISNULL(NULLIF(Col3,0),1)
3
Vijay Bansal

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

أنا أبحث في حساب عدد دورات المخزون التي تحدث في فترة ثلاثة أشهر. لقد حسبت أن لدي تكلفة من البضائع المباعة خلال فترة الثلاثة أشهر البالغة 1000 دولار. المعدل السنوي للمبيعات هو 4000 دولار (1000/3 دولار) * 12. جرد البداية هو 0. جرد النهاية هو 0. متوسط ​​المخزون الخاص بي الآن 0. لدي مبيعات بقيمة 4000 دولار في السنة ، وليس لدي مخزون. هذا يعطي عدد لا حصر له من المنعطفات. هذا يعني أن جميع مخزوني يجري تحويله وشرائه من قبل العملاء.

هذه هي القاعدة التجارية لكيفية حساب يتحول المخزون.

2
Jimmy

لا يوجد إعداد عالمي سحري "إيقاف القسمة على 0 استثناءات". يجب أن يتم تنفيذ العملية ، لأن المعنى الرياضي لـ x/0 يختلف عن المعنى NULL ، لذلك لا يمكن إرجاع NULL. أفترض أنك تهتم بما هو واضح وأن استفساراتك تحتوي على شروط من شأنها أن تقضي على السجلات باستخدام المقسوم على 0 ولا تقوم أبدًا بتقييم التقسيم. 'getcha' المعتاد هو أن معظم المطورين يتوقعون من SQL أن تتصرف مثل اللغات الإجرائية وأن تقدم دائرة قصر التشغيل للمشغل المنطقي ، لكنهالا. أنصحك بقراءة هذا المقال: http://www.sqlmag.com/Articles/ArticleID/9148/pg/2/2.html

2
Remus Rusanu

تصفية البيانات باستخدام جملة حيث بحيث لا تحصل على 0 قيم.

1
nunespascal
CREATE FUNCTION dbo.Divide(@Numerator Real, @Denominator Real)
RETURNS Real AS
/*
Purpose:      Handle Division by Zero errors
Description:  User Defined Scalar Function
Parameter(s): @Numerator and @Denominator

Test it:

SELECT 'Numerator = 0' Division, dbo.fn_CORP_Divide(0,16) Results
UNION ALL
SELECT 'Denominator = 0', dbo.fn_CORP_Divide(16,0)
UNION ALL
SELECT 'Numerator is NULL', dbo.fn_CORP_Divide(NULL,16)
UNION ALL
SELECT 'Denominator is NULL', dbo.fn_CORP_Divide(16,NULL)
UNION ALL
SELECT 'Numerator & Denominator is NULL', dbo.fn_CORP_Divide(NULL,NULL)
UNION ALL
SELECT 'Numerator & Denominator = 0', dbo.fn_CORP_Divide(0,0)
UNION ALL
SELECT '16 / 4', dbo.fn_CORP_Divide(16,4)
UNION ALL
SELECT '16 / 3', dbo.fn_CORP_Divide(16,3)

*/
BEGIN
    RETURN
        CASE WHEN @Denominator = 0 THEN
            NULL
        ELSE
            @Numerator / @Denominator
        END
END
GO
1
Gregory Hart

في بعض الأحيان ، قد لا يكون 0 مناسبًا ، ولكن أحيانًا 1 غير مناسب أيضًا. في بعض الأحيان ، قد تكون القفزة من 0 إلى 1000000000 الموصوفة بأنها تغيير بنسبة 100 في المائة مضللة. 100،000،000 في المئة قد يكون مناسبا في هذا السيناريو. يعتمد ذلك على نوع الاستنتاجات التي تنوي استنباطها بناءً على النسب المئوية أو النسب.

على سبيل المثال ، قد يعني عنصر صغير جدًا بيع ينتقل من 2-4 المباعة وتغيير عنصر كبير للغاية بيع من 1،000،000 إلى 2،000،000 بيع أشياء مختلفة للغاية لمحلل أو للإدارة ، ولكن سيأتي كل من 100 ٪ أو 1 يتغيرون.

قد يكون عزل قيم NULL أسهل من البحث عن مجموعة من الصفوف 0٪ أو 100٪ مختلطة مع البيانات الشرعية. في كثير من الأحيان ، يمكن أن يشير 0 في المقام إلى خطأ أو قيمة مفقودة ، وقد لا ترغب في ملء قيمة عشوائية فقط لتبدو مجموعة البيانات الخاصة بك مرتبة.

CASE
     WHEN [Denominator] = 0
     THEN NULL --or any value or sub case
     ELSE [Numerator]/[Denominator]
END as DivisionProblem
0
Joeyslaptop

يمكنك التعامل مع الخطأ بشكل مناسب عندما ينتشر مرة أخرى إلى برنامج الاتصال (أو يتجاهله إذا كان هذا هو ما تريد). في C # ، ستلقي الأخطاء التي تحدث في SQL استثناءًا يمكنني ملاحظته ثم التعامل معه في التعليمات البرمجية الخاصة بي ، تمامًا مثل أي خطأ آخر.

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

try
{
    Database.ComputePercentage();
}
catch (SqlException e)
{
    // now you can handle the exception or at least log that the exception was thrown if you choose not to handle it
    // Exception Details: System.Data.SqlClient.SqlException: Divide by zero error encountered.
}
0
Despertar

استخدم NULLIF(exp,0) ولكن بهذه الطريقة - NULLIF(ISNULL(exp,0),0)

NULLIF(exp,0) فواصل إذا كانت exp null لكن NULLIF(ISNULL(exp,0),0) لن تنقطع

0
Johnny Kancharla