web-dev-qa-db-ja.com

SQLでの「ゼロ除算」エラーを回避する方法

私はこのエラーメッセージがあります:

メッセージ8134、レベル16、状態1、行1除算ゼロエラーが発生しました。

二度とこのエラーメッセージが表示されないようにSQLコードを書くための最良の方法は何ですか?

私は次のいずれかを実行できます。

  • 除数がゼロにならないようにwhere句を追加します。

または

  • ゼロのための特別な扱いがあるように、caseステートメントを追加することができます。

NULLIF句を使用する最善の方法はありますか?

もっと良い方法はありますか、それともどのようにこれを実施できますか?

306

「ゼロによる除算」エラーを回避するために、次のようにプログラムしました。

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

しかし、これを実行するためのより優れた方法は次のとおりです。

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

"/"キーを使用した場合、唯一の問題はNullIfビットを覚えていることです。

553

ゼロを返したい場合、ゼロの区切りが発生する場合、以下を使用できます。

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

ゼロである除数ごとに、結果セットでゼロが取得されます。

164
Tobias Domhan

これは私のデータで起こるゼロによる除算に対処しようとするときの私の状況に対する最善の解決策のようです。

さまざまな学校のクラブの男性と女性の比率を計算したいが、女性がいないロードオブザリングクラブの比率を計算しようとすると、次のクエリが失敗し、ゼロ除算エラーが発生することがわかります:

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

ゼロによる除算を避けるためにNULLIF関数を使うことができます。 NULLIFは2つの式を比較し、それらが等しい場合はnullを返し、そうでない場合は最初の式を返します。

クエリを次のように書き換えます。

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を返すことを提唱しているように見えた、Edwardoのような懸念に向けられていました。

答え:ここで根本的な問題があると思います。それは0による除算は合法ではないということです。それは何かが基本的に間違っていることを示しています。あなたがゼロで割っているなら、あなたは数学的に意味を成さない何かをしようとしているので、あなたが得ることができる数字の答えは有効ではないでしょう。 (この場合はnullを使用するのが妥当です。これは、後の数学的計算で使用される値ではないためです)。

そのため、Edwardo氏はコメントに「ユーザーが0を入力したらどうしますか?」と尋ね、0を返すのは問題ないと主張しています。ユーザーが金額に0を入力し、そのときに0が返されるようにする場合は、ビジネスルールレベルでコードを入力してその値を取得し、0を返す必要があります。 0.

それは微妙な違いですが、それは重要です...次回誰かがあなたの関数を呼び出してそれが正しいことをすることを期待し、それは数学的に正しくない何かファンキーなことをするので後で誰かに噛む可能性が高いです。あなたは本当に0で割っているのではありません...悪い質問に対して悪い答えを返しているだけです。

私が何かをコーディングしているのを想像してみてください。私は放射線測定のスケーリング値を読み込む必要がありますが、予期しないEdgeの場合は、0を読み込みます。次に、自分の関数に自分の値を入れます...あなたは私に0を返します!万歳、放射線なし!それが本当にそこにあり、それが私が悪い値を渡していたということだけであることを除いて...しかし、私にはわかりません。それは何かが間違っているというフラグですので、私は除算がエラーをスローしたい。

31
Beska

ゼロによる除算がある場合は、少なくともエラーによるクエリの中断を止めてNULLを返すことができます。

SELECT a / NULLIF(b, 0) FROM t 

しかし、私は _決して_ これをcoalesceでゼロに変換したくありません。これは数学的な意味ではまったく間違っています。また、アプリケーションが誤った、誤解を招くような結果を返す可能性があるため、さらに危険です。

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

Nullif()でゼロを捕捉し、次にisnull()で結果のnullを捕捉することで、ゼロ除算エラーを回避できます。

21
Gurufaction

「ゼロで割る」をゼロで置き換えることは物議を醸しています - それだけではありません。場合によっては、1と置き換えるのが(適度に)適切です。よく使う

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

スコア/カウントの変化を調べているときに、データがない場合はデフォルトの1にしたい。例えば

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

多くの場合、私は実際にどこか他の場所でこの比率を計算しました(それは低分母のためにいくつかの非常に大きな調整係数を投げることができるからです。しかし時々 'ハック'は適切です。

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. Divisorをゼロ以外の値にするCHECK制約を追加しました
  2. ユーザーがこのフィールドにゼロ値を入力できないように、フォームにバリデータを追加します。
3
finnw

更新SQLの場合

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

これはあなたがゼロで割ることができる状況です。ビジネスルールは、在庫回転数を計算するために、一定期間の売上原価を取得し、年換算することです。年換算数を取得したら、その期間の平均在庫で割ります。

3か月間に発生する在庫回転数の計算を検討しています。私は、3か月の間に売上原価が1,000ドルであると計算しました。年間売上高は4,000ドル(1,000ドル/ 3)* 12です。最初の在庫は0です。最後の在庫は0です。私の平均在庫は0です。年間売上は4000ドルで、在庫はありません。これは無限のターン数を生み出します。つまり、私の在庫はすべて顧客によって変換および購入されています。

これは、在庫回転数の計算方法に関するビジネスルールです。

2
Jimmy

魔法のようなグローバル設定「0による除算の例外をオフにする」はありません。 x/0の数学的な意味はNULLの意味とは異なるため、NULLを返すことができないため、操作はスローする必要があります。私はあなたが明白な面倒を見ていて、あなたの質問が0の除数を持つレコードを排除しなければならない条件を持っていて、除算を決して評価しないと仮定します。通常の「落とし穴」は、ほとんどの開発者がSQLが手続き型言語のように振る舞い、論理演算子短絡を提供することを期待するよりもですが、NOTを行います。この記事を読むことをお勧めします: http://www.sqlmag.com/Articles/ArticleID/9148/pg/2/2.html

2
Remus Rusanu

値が0にならないように、where句を使用してデータを除外します。

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はまた適切ではないです。 1または100パーセントの変化として説明される0から100,000,000へのジャンプも誤解を招く可能性があります。そのシナリオでは、100,000,000パーセントが適切な場合があります。割合や比率に基づいてどのような結論を導こうとしているかによって異なります。

たとえば、2〜4個の売れ行きの良い商品と、100万〜2,000,000枚の売れ行きの非常に売れている商品は、アナリストや経営陣にとって非常に異なることを意味しますが、どちらも100%または1になります。変化する。

正当なデータが混在している0%または100%の行を検索するよりも、NULL値を分離する方が簡単な場合があります。多くの場合、分母の0はエラーや欠損値を示している可能性があります。データセットをきれいにするためだけに任意の値を入力したくない場合があります。

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

エラーが呼び出し側プログラムに伝播して戻ってきたときに適切にエラーを処理できます(または必要な場合は無視してください)。 C#では、SQLで発生したエラーは、他のエラーと同様に、私が捕捉してコード内で処理できる例外をスローします。

私はあなたがエラーを隠したくないという点でBeskaに同意します。あなたは原子炉を扱っていないかもしれませんが、一般にエラーを隠すことは悪いプログラミング習慣です。これが、現代のほとんどのプログラミング言語が実際の戻り値をエラー/ステータスコードで切り離すための構造化例外処理を実装している理由の1つです。あなたが数学をしているとき、これは特に本当です。最大の問題は、正しく計算された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)

expがnullだがNULLIF(exp,0)は壊れるがNULLIF(ISNULL(exp,0),0)は壊れない

0

これは私がそれを修正した方法です:

IIF(ValueA!= 0、合計/ ValueA、0)

アップデートでラップすることができます:

SET Pct = IIF(ValueA!= 0、合計/ ValueA、0)

または選択で:

SELECT IIF(ValueA!= 0、Total/ValueA、0)AS Pct FROM Tablename;

考え?

0
CorvetteGuru