11

SQLクエリで「ゼロ除算」(メッセージ8134)が発生する理由を理解しようとしていますが、何かが足りないに違いありません。以下の特定のケースの理由を知りたいのですが、私は探していませんNULLIF。または、CASE WHEN...すでに知っているのと同じようなものです(もちろん、以下のような状況で使用できます)。

次のような計算列を持つSQLステートメントがあります

SELECT
    TotalSize,
    FreeSpace,
    (FreeSpace / TotalSize * 100)
FROM
    tblComputer
...[ couple of joins ]...
WHERE
    SomeCondition = SomeValue

このステートメントを実行すると、上記のエラーメッセージでエラーが発生しますが、それ自体は問題ではありません。明らかTotalSizeに0である可能性があり、したがってエラーが発生する可能性があります。

今私が理解していないのはTotalSize、計算された列をコメントアウトするときに列が0である行がないということです。これが当てはまらないことを、再確認しました。

次に、何らかの理由で、where句の条件で実際にフィルタリングするに、結果セット全体に対して列の計算が実行されると思いましたが、これはa)意味がなく、b)エラーをaで再現しようとするとテストセットアップすべてが正常に機能します(以下を参照):

INSERT INTO tblComputer (ComputerName, IsServer) VALUES ('PC0001',1)
INSERT INTO tblComputer (ComputerName, IsServer) VALUES ('PC0002',1)
INSERT INTO tblComputer (ComputerName, IsServer) VALUES ('PC0003',1)
INSERT INTO tblComputer (ComputerName, IsServer) VALUES ('PC0004',0)
INSERT INTO tblComputer (ComputerName, IsServer) VALUES ('PC0005',1)
INSERT INTO tblComputer (ComputerName, IsServer) VALUES ('PC0006',0)
INSERT INTO tblComputer (ComputerName, IsServer) VALUES ('PC0007',1)

INSERT INTO tblHDD (ComputerID, TotalSize, FreeSpace) VALUES (1,100,21)
INSERT INTO tblHDD (ComputerID, TotalSize, FreeSpace) VALUES (2,100,10)
INSERT INTO tblHDD (ComputerID, TotalSize, FreeSpace) VALUES (3,100,55)
INSERT INTO tblHDD (ComputerID, TotalSize, FreeSpace) VALUES (4,0,10)
INSERT INTO tblHDD (ComputerID, TotalSize, FreeSpace) VALUES (5,100,23)
INSERT INTO tblHDD (ComputerID, TotalSize, FreeSpace) VALUES (6,100,18)
INSERT INTO tblHDD (ComputerID, TotalSize, FreeSpace) VALUES (7,100,11)

-- This statement does not throw an error as apparently the row for ComputerID 4 
-- is filtered out before computing the (FreeSpace / TotalSize * 100)
SELECT 
TotalSize,
FreeSpace,
(FreeSpace / TotalSize * 100)
FROM 
tblComputer
JOIN
tblHDD ON
tblComputer.ID = tblHDD.ComputerID
WHERE
IsServer = 1

私はかなり困惑していて、その理由を知りたいです。

事前のおかげで、正しい方向へのアイデアやポインタは大歓迎です

アップデート

これまでのご意見ありがとうございましたが、残念ながら問題の根本に近づいていないようです。ステートメントを少し削除して、1つのJOINを削除してもエラーなしで実行できる場合があります(一時的に削除した出力の追加の列に必要になります)。

JOINを使用するとエラーが発生する理由がわかりませんが、標準のINNER JOINは常に同じ行数以下を返すのではなく、それ以上を返すことはありませんか?

作業コード

SELECT 
TotalSize,
FreeSpace
((FreeSpace / TotalSize) * 100)
FROM 
MyTable1
INNER JOIN 
MyTable2 ON
MyTable1.ID = MyTable2.Table1ID
WHERE 
SomeCondition

コードの原因となるエラー

SELECT 
TotalSize,
FreeSpace
((FreeSpace / TotalSize) * 100)
FROM 
MyTable1
INNER JOIN 
MyTable2 ON
MyTable1.ID = MyTable2.Table1ID
-- This JOIN causes "divide by zero encountered" error
INNER JOIN 
MyTable3 ON
MyTable2.ID = MyTable3.Table2ID
WHERE 
SomeCondition

また、カーソルを使用して結果を行ごとにループして運試しをしましたが、その場合はエラーは発生しませんでした(上記の2つのステートメントのどちらを試しても)。

コードのインデントが乱雑になって申し訳ありませんが、どういうわけか正しいフォーマットが適用されていないようです。

G。

4

4 に答える 4

4

SQLServerが単一のSELECTステートメントを処理するために使用する基本的な手順は次のとおりです。

  1. パーサーはSELECTステートメントをスキャンし、キーワード、式、演算子、識別子などの論理単位に分割します。
  2. シーケンスツリーと呼ばれることもあるクエリツリーは、ソースデータを結果セットに必要な形式に変換するために必要な論理ステップを記述して構築されます。
  3. クエリオプティマイザは、ソーステーブルにアクセスできるさまざまな方法を分析します。次に、より少ないリソースを使用しながら結果を最も速く返す一連のステップを選択します。クエリツリーが更新され、この正確な一連のステップが記録されます。クエリツリーの最終的な最適化されたバージョンは、実行プランと呼ばれます。
  4. リレーショナルエンジンは実行プランの実行を開始します。ベーステーブルからのデータを必要とするステップが処理されると、リレーショナルエンジンは、ストレージエンジンがリレーショナルエンジンから要求された行セットからのデータを渡すように要求します。
  5. リレーショナルエンジンは、ストレージエンジンから返されたデータを結果セットに定義された形式に処理し、結果セットをクライアントに返します。

私の解釈では、すべての行の計算列を評価する前に、where句が評価される保証はありません

以下のようにクエリを変更し、計算の前にwhere句を強制的に評価することで、その仮定を確認できます。

SELECT
    TotalSize,
    FreeSpace,
    (FreeSpace / TotalSize * 100)
FROM (
  SELECT
      TotalSize,
      FreeSpace,
  FROM
      tblComputer
  ...[ couple of joins ]...
  WHERE
      SomeCondition = SomeValue
  ) t
于 2011-03-04T09:43:41.283 に答える
1

実行すると返される行は次のとおりです。

SELECT
   TotalSize
FROM
   tblComputer
   ...[ couple of joins ]...
WHERE
   SomeCondition = SomeValue
   and ((TotalSize * 100) = 0)

これにより、SQL Server が (TotalSize * 100) をゼロと評価する方法の手がかりが得られる場合があります。

もう 1 つのアイデアですが、where ステートメントに問題となる可能性のあるものはありますか?
あなたはそれが TotalSize であると想定していますが、それは別の場所にある可能性があります。

于 2011-03-04T09:15:05.927 に答える
0

私は同じ問題に遭遇していました。私の場合、NULL は許容されたので、次の方法で修正できました。

Select Expression1 / Expression2 -- Caused Division By 0
Select Expression1 / NULLIF(Expression2,0) -- Causes result to be NULL

他の処理が必要な場合は、次のように式全体を ISNULL 関数でラップできます。

Select ISNULL(Expression1 / NULLIF(Expression2,0)-5) -- Returns -5 instead of null or divide by 0
于 2012-01-31T20:54:37.120 に答える