私はこのエラーメッセージを持っています:
メッセージ8134、レベル16、状態1、行1ゼロで除算エラーが発生しました。
このエラーメッセージが二度と表示されないようにSQLコードを作成する最良の方法は何ですか?
次のいずれかを実行できます。
- where句を追加して、除数がゼロにならないようにします
または
- ゼロの特別な扱いがあるように、ケースステートメントを追加できます。
NULLIF句を使用する最良の方法はありますか?
より良い方法はありますか、またはこれをどのように実施できますか?
私はこのエラーメッセージを持っています:
メッセージ8134、レベル16、状態1、行1ゼロで除算エラーが発生しました。
このエラーメッセージが二度と表示されないようにSQLコードを作成する最良の方法は何ですか?
次のいずれかを実行できます。
または
NULLIF句を使用する最良の方法はありますか?
より良い方法はありますか、またはこれをどのように実施できますか?
回答:
「ゼロによる除算」エラーを回避するために、次のようにプログラムしました。
Select Case when divisor=0 then null
Else dividend / divisor
End ,,,
しかし、ここにそれを行うより良い方法があります:
Select dividend / NULLIF(divisor, 0) ...
ここでの唯一の問題は、「/」キーを使用した場合、NullIfビットを覚えておくことです。
IsNull代わりに誤って使用していませんNullIfか?ぜひお試しください!SELECT Value,1/NullIf(Value,0)FROM(VALUES(0),(5.0),(NULL))x(Value);「ブレーク」しない限り、NULLを返すという意味ですか?IsNullまたはを使用して、必要なものに変換できますCoalesce。
SELECT 1 / NULLIF(NULL, 0)失敗しますが、それNULLIF()は最初の引数のデータ型を知る必要があるためです。この変更された例は正常に機能しますSELECT 1 / NULLIF(CAST(NULL AS INT), 0)。実際にNULLIF()は、NULL定数ではなくテーブル列を指定します。テーブルの列には既知のデータ型があるため、これも正常に機能しますSELECT 1 / NULLIF(SomeNullableColumn, 0) FROM SomeTable。
0を返したい場合、0のデビジョンが発生する場合は、以下を使用できます。
SELECT COALESCE(dividend / NULLIF(divisor,0), 0) FROM sometable
ゼロであるすべての除数について、結果セットでゼロを取得します。
さまざまな学校のクラブの男性と女性の比率を計算したいが、女性のいないロードオブザリングクラブの比率を計算しようとすると、次のクエリが失敗し、ゼロ除算エラーが発生するとします。 :
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、そしてエラーは生成されません。
select males/(males+females), females/(males+females)。これにより、男性の31%、女性の69%など、クラブの男性と女性の割合分布がわかります。
クエリの最初でこれを行うこともできます。
SET ARITHABORT OFF
SET ANSI_WARNINGS OFF
したがって、そのような100/0ものがあれば、NULLを返します。私はこれを単純なクエリに対してのみ行ったので、それがより長い/複雑なクエリにどのように影響するのかわかりません。
編集:私は最近これについて多くの反対票を得ています...だから、質問が最新の編集を受ける前にこの回答が書かれていて、オプションとしてnullを返すことが強調表示されているというメモを追加したいと思いました。 。これは非常に許容できるようです。私の回答の一部は、0を返すことを主張しているように思われたコメントで、エドワードのような懸念に対処しました。これは、私が反対しているケースです。
回答:ここには根本的な問題があると思います。つまり、0による除算は合法ではないということです。これは、何かが根本的に間違っていることを示しています。ゼロで除算している場合は、数学的に意味のないことを実行しようとしているため、取得できる数値の答えは有効ではありません。(この場合のnullの使用は、後の数学計算で使用される値ではないため、妥当です)。
したがって、Edwardoはコメントで「ユーザーが0を入力した場合はどうなるのか」と質問し、代わりに0を取得しても問題ないと主張します。ユーザーが金額にゼロを入力し、そのときに0が返されるようにしたい場合は、ビジネスルールレベルでコードを入力してその値をキャッチし、0を返す必要があります... 0による除算の特別なケースはありません= 0。
それは微妙な違いですが、それは重要です...次回誰かがあなたの関数を呼び出して、それが正しいことをすることを期待し、数学的に正しくないファンキーなことをするので、それは特定のエッジケースを処理するだけです後で誰かを噛む可能性が高い。本当に0で除算しているわけではありません...悪い質問に対して悪い答えを返すだけです。
私が何かをコーディングしていると想像してください。私は放射測定のスケーリング値を読み取る必要がありますが、予期しない奇妙なエッジの場合は0を読み取ります。次に、値を関数にドロップします...あなたは私に0を返します!ばんざーい!それが本当にそこにあるのを除いて、それは私が悪い値を渡していたというだけです...しかし、私にはわかりません。それは何かが間違っているというフラグだからです。
「ゼロで除算」をゼロで置き換えることは議論の余地があります-しかし、それだけが唯一のオプションではありません。場合によっては、1で置き換えるのが(合理的に)適切です。私はよく自分自身を使っている
ISNULL(Numerator/NULLIF(Divisor,0),1)
スコア/カウントのシフトを見ていて、データがない場合はデフォルトで1にしたい場合。例えば
NewScore = OldScore * ISNULL(NewSampleScore/NULLIF(OldSampleScore,0),1)
ほとんどの場合、私は実際にこの比率を別の場所で計算しました(特に、低分母に対して非常に大きな調整係数をスローする可能性があるためです。この場合、通常、OldSampleScoreの制御はしきい値よりも大きいため、ゼロは除外されます。ただし、「ハッキング」が適切な場合もあります。
私はしばらく前に、ストアドプロシージャ用に関数を記述しました。
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
Divisor的にゼロ以外になるCHECK制約を追加します更新SQLの場合:
update Table1 set Col1 = Col2 / ISNULL(NULLIF(Col3,0),1)
「0除算による除算をオフにする」という魔法のグローバル設定はありません。x / 0の数学的な意味はNULLの意味とは異なるため、操作はスローする必要があるため、NULLを返すことはできません。私はあなたが明白なことに気をつけていると思います、そしてあなたのクエリは0除数でレコードを排除し、除算を決して評価すべきでない条件を持っています。通常の「問題」は、ほとんどの開発者がSQLが手続き型言語のように動作し、論理演算子のショートサーキットを提供することを期待するものですが、そうではありません。この記事を読むことをお勧めします:http : //www.sqlmag.com/Articles/ArticleID/9148/pg/2/2.html
これは、ゼロで除算できる状況です。ビジネスルールは、在庫回転率を計算するために、ある期間の販売された商品のコストを取り、それを年間化することです。年換算値を取得したら、その期間の平均在庫で割ります。
3か月間に発生する在庫回転数を計算しています。私は、3か月の間に$ 1,000の商品原価を販売したと計算しました。年間売上高は$ 4,000($ 1,000 / 3)* 12です。最初の在庫は0です。最後の在庫は0です。私の平均在庫は現在0です。年間$ 4000の売上があり、在庫がありません。これにより、無限の回転数が得られます。これは、すべての在庫が顧客によって変換および購入されていることを意味します。
これは、在庫回転率の計算方法のビジネスルールです。
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
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
これは私がそれを修正した方法です:
IIF(値A!= 0、合計/値A、0)
更新でラップすることができます:
SET Pct = IIF(ValueA!= 0、Total / ValueA、0)
または選択で:
SELECT IIF(ValueA!= 0、Total / ValueA、0)AS Pct FROM Tablename;
考え?
エラーが呼び出し側プログラムに伝播するときに、エラーを適切に処理できます(必要な場合は無視します)。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.
}
使用するNULLIF(exp,0)が、このように-NULLIF(ISNULL(exp,0),0)
NULLIF(exp,0)expがある場合には休憩nullが、NULLIF(ISNULL(exp,0),0)中断されません