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


363

私はこのエラーメッセージを持っています:

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

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

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

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

または

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

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

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


7
おそらく、いくつかのデータ検証が必要です。
アンソニー、

回答:


645

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

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

しかし、ここにそれを行うより良い方法があります:

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

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


13
はるかに優れた方法[除数を選択/ nullif(divisor、0)...]は、除数がNULLの場合に機能しなくなります。
アンダーソン

8
@アンダーソンそれは全く真実ではありません。のIsNull代わりに誤って使用していませんNullIfか?ぜひお試しください!SELECT Value,1/NullIf(Value,0)FROM(VALUES(0),(5.0),(NULL))x(Value);「ブレーク」しない限り、NULLを返すという意味ですか?IsNullまたはを使用して、必要なものに変換できますCoalesce
ErikE 2017年

1
@ErikE、それは真実です...実行してみてください... select 1 / nullif(null、0)...あなたは "最初の引数の型が知られるように」「coalesce(FieldName、0)」を使用してこれを処理します...たとえば、select 1 / nullif(coalesce(null、0)、0)
John Joseph

1
@JohnJosephあなたが私に同意しているか、私と議論しているかはわかりません。
ErikE

1
@JohnJoseph取得したエラーをよく見てください。はい、SELECT 1 / NULLIF(NULL, 0)失敗しますが、それNULLIF()は最初の引数のデータ型を知る必要があるためです。この変更された例は正常に機能しますSELECT 1 / NULLIF(CAST(NULL AS INT), 0)。実際にNULLIF()は、NULL定数ではなくテーブル列を指定します。テーブルの列には既知のデータ型があるため、これも正常に機能しますSELECT 1 / NULLIF(SomeNullableColumn, 0) FROM SomeTable
MarredCheese

180

0を返したい場合、0のデビジョンが発生する場合は、以下を使用できます。

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

ゼロであるすべての除数について、結果セットでゼロを取得します。


9
いくつかのベンチマークは、COALESCEがISNULLよりも少し遅いことを示しています。ただし、COALESCEは標準に準拠しているため、移植性が高くなります。
Paul

37
これが機能する理由を他の誰かがすぐに理解できない場合、dが0の場合、NULLIF(d、0)はNULLを返します。SQLでは、NULLで除算するとNULLが返されます。合体0によって得NULL置き換え
GuiSim

16
@SQLGeorge私はあなたの主張に同意しますが、数学的に正しいよりも統計的に正しいものを重視する場合があることに注意してください。統計関数を使用する場合、除数がゼロの場合、0または1でも許容できる結果になります。
アサフード2016

10
なぜこれが悪いのか誰かが私に説明できますか?パーセントを計算しようとしていて、除数がゼロの場合、結果がゼロパーセントになることを望んでいます。
トッドシャープ

10
@Georgeと@ James / Wilsonは、質問されている質問を根本的に誤解していると思います。数学的な観点からは技術的に正しくなくても、「0」を返すことが適切なビジネスアプリケーションは確かにあります。
Sean Branchaw 2016

66

これは、私のデータで発生する、ゼロによる除算に対処しようとするときの私の状況に対する最良の修正であるように思われました。

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

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

この関数NULLIFを使用して、ゼロによる除算を回避できます。NULLIF2つの式を比較し、等しい場合はnull、それ以外の場合は最初の式を返します。

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

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

割った任意の数はNULL与えNULL、そしてエラーは生成されません。


6
はい確かに、それはあるWAY BETTER非常に多くのupvotesを持っていること、他の答えより。ソリューションでは、少なくともNULLがあり、正しい結果を提供できないことを示しています。しかし、結果をNULLからゼロに変換すると、単に間違った誤解を招く結果になります。
SQLポリス

8
ちなみに、男性/女性の比率を計算する場合は、次のように合計と比較することをお勧めしますselect males/(males+females), females/(males+females)。これにより、男性の31%、女性の69%など、クラブの男性と女性の割合分布がわかります。
SQLポリス

44

クエリの最初でこれを行うこともできます。

SET ARITHABORT OFF 
SET ANSI_WARNINGS OFF

したがって、そのような100/0ものがあれば、NULLを返します。私はこれを単純なクエリに対してのみ行ったので、それがより長い/複雑なクエリにどのように影響するのかわかりません。


1
私のために働く。私の場合、WHERE句で除算演算を使用する必要があります。私がWHEREアウトをコメントアウトすると、結果にゼロ値がないため、ゼロ除算器がないと確信しています。しかし、クエリオプティマイザーは、フィルター処理中に何らかの理由でゼロで除算します。SET ARITHABORT OFF SETとANSI_WARNINGS OFFは機能します-WHERE句でゼロ除算との2日間の戦いの後。THX!
huhu78 2012

2
これは「汚い」と感じますが、私はそれが大好きです!結果を完全に変更するGROUP BYにその列を追加する必要があったため、集計を実行し、CASEステートメントを使用するクエリで必要でした。最初のクエリを副選択にしてから、外側のクエリでGROUP BYを実行すると、除算が含まれるため、結果も変更されます。
Andrew Steitz 2013年

1
OK、それでも私はこの「解決策」が好きですが、多くの人がおそらく感じているように、「よりクリーンな」方法が必要だと感じました。警告を再度有効にするのを忘れた場合はどうなりますか?または、誰かが私のコードをクローンし(それは決して起こりませんよね?)、警告について考えていませんでしたか?とにかく、NULLIF()に関する他の回答を見ました。NULLIF()については知っていましたが、NULLで除算するとNULLが返されることに気づきませんでした(エラーになると思いました)。だから...私は次のように行きました:ISNULL((SUM(foo)/ NULLIF(SUM(bar)、0))、0)AS Avg
Andrew Steitz

2
私はこの解決策を知りませんでした。気に入るとは思いませんが、いつか知っておくと便利かもしれません。どうもありがとうございました。
Henrik Staun Poulsen

1
これが最も簡単なソリューションですが、パフォーマンスが低下することに注意してください。docs.microsoft.com/en-us/sql/t-sql/statements/…から:「ARITHABORTをOFFに設定すると、クエリの最適化に悪影響を及ぼし、パフォーマンスの問題につながる可能性があります。」
モノブレイン

36

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

SELECT a / NULLIF(b, 0) FROM t 

しかし、私は考えNEVERでこれをゼロに変換しないcoalesce、それが多くのupvotesを持っている他の回答で示されているように。これは数学的な意味で完全に間違っており、アプリケーションが誤って誤解を招く結果を返す可能性があるため、危険ですらあります。


32

編集:私は最近これについて多くの反対票を得ています...だから、質問が最新の編集を受ける前にこの回答が書かれていて、オプションとしてnullを返すことが強調表示されているというメモを追加したいと思いました。 。これは非常に許容できるようです。私の回答の一部は、0を返すことを主張しているように思われたコメントで、エドワードのような懸念に対処しました。これは、私が反対しているケースです。

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

したがって、Edwardoはコメントで「ユーザーが0を入力した場合はどうなるのか」と質問し、代わりに0を取得しても問題ないと主張します。ユーザーが金額にゼロを入力し、そのときに0が返されるようにしたい場合は、ビジネスルールレベルでコードを入力してその値をキャッチし、0を返す必要があります... 0による除算の特別なケースはありません= 0。

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

私が何かをコーディングしていると想像してください。私は放射測定のスケーリング値を読み取る必要がありますが、予期しない奇妙なエッジの場合は0を読み取ります。次に、値を関数にドロップします...あなたは私に0を返します!ばんざーい!それが本当にそこにあるのを除いて、それは私が悪い値を渡していたというだけです...しかし、私にはわかりません。それは何かが間違っているというフラグだからです。


15
同意しません。あなたのビジネスルールは、決して違法な数学をしてはいけません。もしあなたがこのようなことをすることになるなら、おそらくあなたのデータモデルは間違っています。あなたは0除算が発生したときはいつでも、あなたは熟考すべきデータがNULLではなく0になっていたかどうかを
レムスRusanu

32
私が「本当のプログラミングをしたことはないか」と尋ねる誰かに反対票を投じられたとは信じられません。怠惰ではなく、正しいことをするように言っているからです。 ため息
Beska、

11
すみません、あなたを怒らせるつもりはありませんでした。しかし、この質問は多くの一般的なLOBアプリケーションで完全に有効であり、「0による除算は合法ではない」と答えることは、IMHOの価値を高めません。
Eduardo Molteni、2009年

2
@JackDouglasそうです。これは、ビジネスルールで特別なケースを特別な方法で処理する必要がある場合ですが、空白を返す基になる数学であってはなりません。それはビジネスルールであるべきです。受け入れられた回答はnullを返します。これは、それを処理するための適切な方法です。例外をスローしても大丈夫です。SQLに行く前にそれを見つけて処理することは間違いなく理想的でしょう。数学的に正しくない値を返す、他のものが呼び出すことができるある種の関数を提供することは、他の呼び出し元には当てはまらない場合があるため、進むべき道ではありません
Beska

4
@JackDouglasはい、それは私が同意する良い要約です。元々、質問は「このエラーを非表示にするために何ができるか」と表現されていたようです。それ以来、それは進化してきました。nullを返す、彼が最終的にたどる答えは、1つの合理的な応答のようです。(私は0またはその他の数値を返さないことを強く主張していました。)
Beska

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

nullをnullif()でキャッチし、結果のnullをisnull()で取得すると、ゼロ除算エラーを回避できます。


3
その長さのため、あなたの回答は削除を推奨されました。提案していることについては、簡単な説明を追加することをお勧めします。たとえそれが非常に単純に見えても、;)
Trinimon

10

「ゼロで除算」をゼロで置き換えることは議論の余地があります-しかし、それだけが唯一のオプションではありません。場合によっては、1で置き換えるのが(合理的に)適切です。私はよく自分自身を使っている

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

スコア/カウントのシフトを見ていて、データがない場合はデフォルトで1にしたい場合。例えば

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

ほとんどの場合、私は実際にこの比率を別の場所で計算しました(特に、低分母に対して非常に大きな調整係数をスローする可能性があるためです。この場合、通常、OldSampleScoreの制御はしきい値よりも大きいため、ゼロは除外されます。ただし、「ハッキング」が適切な場合もあります。


1
@Nメイソン; はい、時々1はオプションです。しかし、バックスラッシュを入力するとき、ISNULLの部分をどのように覚えていますか?
Henrik Staun Poulsen

1
申し訳ありませんが、質問を理解できません。
Nメイソン

6

私はしばらく前に、ストアドプロシージャ用に関数を記述しました。

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

2
こんにちはRon、ニースのソリューションです。ただし、データ型(小数点以下4桁)が制限されており、@ divisorも負の値になる場合があります。そして、それをどのように使用することを強制しますか?TIA Henrik Staun Poulsen
Henrik Staun Poulsen

1
そのときは、特定の問題のシナリオを処理するために、それをかなりすばやく打ち切りました。単一の開発者アプリなので、私の記憶を除いて、施行はそれほど難しくありません。:-)
ロン・サベージ

5
printステートメントにもかかわらず、これはストアドプロシージャではなく、スカラーUDFです。これは、クエリの一部である場合、MS-SQLで強制終了します。
Mark Sowul、2012年

4
私は、スカラー関数が痛みを引き起こすというMark Sowulの主張に同意しました。これはT-SQLのひどい提案です。やめてください。スカラー関数はパフォーマンスを破壊するものです!インラインテーブル値関数は、SQL Serverで唯一の優れたユーザー関数です(適切に実行できるCLR関数を除いて)。
ダヴォス

4
  1. 強制Divisor的にゼロ以外になるCHECK制約を追加します
  2. フォームにバリデーターを追加して、ユーザーがこのフィールドにゼロの値を入力できないようにします。

1
CHECK制約がますます好きになり始めました。
Henrik Staun Poulsen、2010

4

更新SQLの場合:

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

3
こんにちはビジェイ、はい、それでうまくいきますが...私はISNULLの部分に注意します。除数がゼロであるため、結果が不明であることをユーザーに知らせたいと思います。
Henrik Staun Poulsen 2015

2
おかげで、複雑なsubQueryで節約できました。
QMaster

3

「0除算による除算をオフにする」という魔法のグローバル設定はありません。x / 0の数学的な意味はNULLの意味とは異なるため、操作はスローする必要があるため、NULLを返すことはできません。私はあなたが明白なことに気をつけていると思います、そしてあなたのクエリは0除数でレコードを排除し、除算を決して評価すべきでない条件を持っています。通常の「問題」は、ほとんどの開発者がSQLが手続き型言語のように動作し、論理演算子のショートサーキットを提供することを期待するものですが、そうではありません。この記事を読むことをお勧めします:http : //www.sqlmag.com/Articles/ArticleID/9148/pg/2/2.html


4
そのような「魔法のグローバル設定」があります; SET ARITHABORT OFF。
デビッドマンハイム

3

これは、ゼロで除算できる状況です。ビジネスルールは、在庫回転率を計算するために、ある期間の販売された商品のコストを取り、それを年間化することです。年換算値を取得したら、その期間の平均在庫で割ります。

3か月間に発生する在庫回転数を計算しています。私は、3か月の間に$ 1,000の商品原価を販売したと計算しました。年間売上高は$ 4,000($ 1,000 / 3)* 12です。最初の在庫は0です。最後の在庫は0です。私の平均在庫は現在0です。年間$ 4000の売上があり、在庫がありません。これにより、無限の回転数が得られます。これは、すべての在庫が顧客によって変換および購入されていることを意味します。

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


3
はい、それであなたは無限のターン数を持ちます。したがって、この場合、ゼロによる除算がある場合は、「#INF」のようなものを表示する必要があります。
SQLポリス

1
「最初の在庫は0です。最後の在庫は0です。私の平均在庫は0になりました。」あなたの計算は推定です。ある時点で在庫がプラスになるか、何も出荷/販売できなくなります。結果として+∞に不満がある場合は、平均在庫のより適切な見積もりを使用してください。
トムブロジェット2017年

2
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

UDFを使用すると、クエリが強制的にシングルスレッドモードで実行されるため、私はあなたのソリューションが好きではありません。私はあなたのテスト設定が好きです。これをすべてのUDFで使用したいと思います。
Henrik Staun Poulsen

私にとって、このソリューションは完璧でエレガントです
Payedimaunt 2017年

@Payedimaunt; はい、UDFは非常にエレガントなコードにつながります。しかし、それはうまく機能しません。これは「睡眠薬のカーソル」です。:-)また、通常の "/"の代わりにdbo.Divideを書くことを覚えておくことも困難です
Henrik


1

0は適切でない場合もありますが、1も適切でない場合があります。場合によっては、0から100,000,000へのジャンプが1または100%の変化であると誤解されることがあります。そのシナリオでは、100,000,000パーセントが適切な場合があります。それは、パーセンテージまたは比率に基づいて、どのような結論を導き出すかによって異なります。

たとえば、販売数が2〜4の非常に少ない販売アイテムと、販売数が1,000,000から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

1
問題は、除算をしたいときはいつでも何かをすることを忘れないことです。CASEまたはNULLIFの追加を忘れてしまった場合は、月曜日の朝にx週間でサポートケースが提供されます。嫌いです。
Henrik Staun Poulsen 2018

1

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

IIF(値A!= 0、合計/値A、0)

更新でラップすることができます:

SET Pct = IIF(ValueA!= 0、Total / ValueA、0)

または選択で:

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

考え?


私や他の人たちは、「unknown」をゼロに置き換えるのは危険な修正だと考えています。私はNULLをずっと好みます。しかし難しいのは、すべての部門にiifまたはnullifを追加することを忘れないことです。
Henrik Staun Poulsen

0

エラーが呼び出し側プログラムに伝播するときに、エラーを適切に処理できます(必要な場合は無視します)。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.
}

1
エラーを0で非表示にすることは解決策ではないことに私たちは皆同意します。私が提案するのは、すべての「/」の後に「NULLIF」が続くようにコードを記述することです。このようにして、私のレポート/原子炉は放置されず、厄介なエラーメッセージ8134の代わりに「NULL」を表示しています。除数が0のときに別のプロセスが必要な場合、ベスカと私は同意します。それをコーディングする必要があります。「/」を書くたびに行うのは難しいだけです。「/ NULLIF」は毎回行うことが不可能ではありません。
Henrik Staun Poulsen 2017

0

使用するNULLIF(exp,0)が、このように-NULLIF(ISNULL(exp,0),0)

NULLIF(exp,0)expがある場合には休憩nullが、NULLIF(ISNULL(exp,0),0)中断されません


1
0がゼロではなく、oの場合、NULLIF(exp、0)を取得できません。試してみてください。DECLARE @i INT SELECT 1 / NULLIF(@i、0)を実行して、それを解除できるかどうかを確認します。
Henrik Staun Poulsen 2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.