2011-12-21 13 views
9

SQL Serverで長年働いてきた曖昧な、おそらくはカーゴカルチャーのメモリがあります。空の列がある場合は、WHERE節を書くのは安全ではありません以下のような述語:それはSQLのルールはショートを規定していないという事実とは何かを持っていたSQLと論理演算子とヌルチェック

... WHERE the_column IS NULL OR the_column < 10 ... 

」(実際には、クエリの最適化の理由から、おそらく悪いアイデア種類-のだという)、したがって、 < "と比較することができます。今、まさに恐ろしいことになるだろう、なぜ、私は知らないが、私は厳しくその「CASE」句として常にコードにいくつかのドキュメントで警告されリコール:

... WHERE 1 = CASE WHEN the_column IS NULL THEN 1 WHEN the_column < 10 THEN 1 ELSE 0 END ... 

(間抜け。「1 =」SQL Serverは/ファーストクラスのブール値を持っていなかったしない、または少なくとも私はそれがなかったと思ったので一部である)

だからここに私の質問は以下のとおりです。

  1. ですそれはSQL Server(またはおそらくSQL Server 2000または2005のバック版)にとって本当に真実ですか?
  2. もしそうなら、同じ警告がPostgreSQLに当てはまりますか? (重要な場合は8.4)
  3. 問題はなんですか?インデックスがどのように機能するのかと関係がありますか?

私のSQLでのアースはかなり弱いです。

+1

おそらく彼らはANDについて話していたでしょうか? nullと何かがnullであるため、式にnull項が含まれる場合は、合体または大文字小文字が必要な場合がよくあります。 –

答えて

10

私はSQL Serverのを知らないので、私はそれに話すことができません。

Expression Evaluation Rules

:いくつかの論理演算子L用の発現a L b考える

は、ab前または後に評価されることやabの両方が評価されるも、保証はありませんサブ式の評価の順序は定義されていません。特に、演算子または関数の入力は、必ずしも左から右または他の固定順序で評価されるとは限らない。

さらに、式の結果がその一部だけを評価することによって決定できる場合、他の部分式はまったく評価されない可能性があります。
[...]
これは、いくつかのプログラミング言語に見られるブール演算子の左から右の「短絡」と同じではないことに注意してください。

したがって、複雑な式の一部として副作用のある関数を使用することは賢明ではありません。 WHEREHAVING句で副作用や評価順序に依存することは特に危険です。これらの句は実行計画の開発の一環として広範囲に再処理されるためです。

限り形式の式のように:

the_column IS NULL OR the_column < 10 

が懸念され、NULL < nは、すべてのnためNULLであってもNULL < NULLNULLに評価するので何も心配することはありません。さらに、NULLは真実ではありませんので、

null is null or null < 10 

true or nullを言うだけの複雑な方法であり、それは関係なく、最初に評価された部分式のtrueです。

"CASE"を使うのは、私にはたいていcargo-cult SQLのように聞こえます。しかし、ほとんどの貨物文化のように、貨物の下に埋もれているカーネルがあります。ちょうどPostgreSQLのマニュアルからの私の最初の抜粋の下に、あなたはこの見つける:それは評価の順序を強制することが不可欠である場合には

を、CASE構築物は、(セクション9.16を参照)を使用することができます。たとえば、これはWHERE句で0除算を避けるの信頼できない方法です:

SELECT ... WHERE x > 0 AND y/x > 1.5; 

は、しかし、これは安全である:

SELECT ... WHERE CASE WHEN x > 0 THEN y/x > 1.5 ELSE false END; 

だから、あなたは警戒する必要がある場合その後、あなたはCASEとして評価の順序を制御するためにCASEを使用する必要があり、例外を発生させたり、他の副作用があります条件はevaluated in orderです:

の条件は、booleanの結果を返す式です。条件の結果がtrueの場合、式の値はであり、残りの式はCASEの式は処理されません。条件の結果が真でない場合、後続のWHEN句は同じ方法で調べられます。

ので、この与えられた:

case when A then Ra 
    when B then Rb 
    when C then Rc 
    ... 

Aは、B前に評価されることが保証CBなどと評価は、すぐに条件のいずれかが真の値に評価されて停止しています。要約すると

CASE短絡の言い訳ANDOR短絡でもないあなただけの副作用から保護する必要がある場合CASEを使用する必要がありますので。

+1

はいありがとうございます。私は、SQLが短絡(あるいは、短絡ではない)ルールを強制しないという事実を理解しています。疑問は、通常の関係比較が多分nullの列に対して評価されるなら、何かがひどいことが起こるかどうかということです。非常に詳細な答えをありがとうございます。 – Pointy

1

このような問題は一度も聞いたことがなく、this bit of SQL Server 2000 documentationは例ではWHERE advance < $5000 OR advance IS NULLを使用しているため、非常に厳しいルールであってはなりません。 ORの唯一の問題は、ANDよりも優先度が低いことです。だから、間違ってWHERE the_column IS NULL OR the_column < 10 AND the_other_column > 20のようなものを書くかもしれません。しかし、通常の解決策は大きなCASEの式ではなく、かっこです。

ほとんどのRDBMSでは、インデックスにはヌル値が含まれていないと思いますので、the_columnのインデックスはこのクエリにはあまり役に立ちません。たとえそうでなかったとしても、なぜ大きな数字CASEの式が索引に適しているのかわかりません。

(もちろん、それは否定を証明するのは難しい、と多分他の誰かが、あなたが参照しているものを知っているのだろうか?)

1

まあ、私はずっと永遠に(ちょっと、私はそのようなクエリを生成するクエリジェネレータを書いた)、最初の例のような質問を繰り返し書いてきました。

誰かがあなたにファンキーな書き込みをしてくれたときの覚え書きを思い出すかもしれないと思いますORです。最初の例では、ORによって結合された条件は、同じテーブルの同じ1つの列を制限します。これは問題ありません。 2番目の条件が結合条件(つまり、2つの異なるテーブルから列を制限していた場合)では、クエリプランナには選択肢がなく、デカルト結合(bad、bad、bad !!! )。

私はあなたのCASE関数が本当に何かをしているとは思っていませんが、質問プランナがクエリの良い実行計画を見つけようとするのを妨げているかもしれません。

しかし、より一般的には、単純なクエリを最初に記述して、それが現実的なデータに対してどのように機能するかを見てください。存在しないかもしれない問題を心配する必要はありません!

0

Nullが混乱する可能性があります。 "... WHERE 1 = CASE ..."は、パラメータとしてNullまたはValueを渡す場合に便利です。 「WHERE the_column = @parameter。この投稿はPassing Null using OLEDB役に立つかもしれません。

1

代わりの

the_column IS NULL OR the_column < 10 

私は

isnull(the_column,0) < 10 

または最初例えば

WHERE 1 = CASE WHEN isnull(the_column,0) < 10 THEN 1 ELSE 0 END ... 
0

にしてくださいCASEが便利なもう1つの例は、varcharカラムで日付関数を使用する場合です.ISDATEをuの前に追加するconvert convert(colA、datetime)が機能しない可能性があります。また、colAに日付以外のデータがあると、クエリでエラーが発生する可能性があります。