SQL WHERE句のブール式 短絡評価 ?
例えば:
SELECT *
FROM Table t
WHERE @key IS NULL OR (@key IS NOT NULL AND @key = t.Key)
@ key IS NULLがtrueと評価される場合、@ key IS NOT NULL AND @key = t.Key評価されましたか?
いいえの場合、なぜですか?
はいの場合、保証されていますか? ANSI SQLの一部ですか、それともデータベース固有ですか?
データベース固有の場合、SqlServer?オラクル? MySQL?
ANSI SQL Draft 2003 5WD-01-Framework-2003-09.pdf
6.3.3.3ルール評価の順序
[...]
優先順位が形式または括弧によって決定されない場合、式の効果的な評価は一般に左から右に実行されます。ただし、実装依存式が実際に左から右に評価されるかどうか、特にオペランドまたは演算子によって条件が発生する可能性がある場合または式の結果が完全に決定されない場合式のすべての部分を評価します。
上記から、短絡は実際には利用できません。
必要な場合は、Caseステートメントをお勧めします。
Where Case when Expr1 then Expr2 else Expr3 end = desiredResult
Expr1
は常に評価されますが、Expr2
およびExpr3
は行ごとに評価されます。
これは、次の3つの理由で、短絡しないように書くケースの1つだと思います。
MSSQLの場合、明らかな場所でBOLを調べても解決しないため、私にとっては、それが標準的に曖昧になります。
少なくともそのときはコードが機能することを知っているからです。そしてもっと重要なのは、私の後に来る人たちも同じことなので、同じ質問を何度も繰り返して心配するように彼らを設定することはありません。
いくつかのDBMS製品については十分な頻度で執筆していますが、簡単に回避できる場合は違いを覚えておく必要はありません。
SQL Server(2005)の短絡が保証されているとは思わない。 SQL Serverは、多くのこと(インデックス、統計、テーブルサイズ、リソースなど)を考慮した最適化アルゴリズムを使用してクエリを実行し、効果的な実行計画を作成します。この評価の後、短絡ロジックが保証されているとは断言できません。
私は先ほど自分で同じ質問にぶつかりましたが、私の研究では決定的な答えは得られませんでした。動作することを証明するために小さなクエリを作成することもできますが、データベースの負荷が増加するにつれて、テーブルが大きくなり、データベース内で物事が最適化および変更されることを確認できますか?ホールド。私はできなかったため、注意を怠って、WHERE句でCASEを使用して短絡を保証しました。
データベースの仕組みを覚えておく必要があります。パラメータ化されたクエリを指定すると、dbは、パラメータの値なしで、そのクエリに基づいて実行プランを作成します。このクエリは、実際に提供された値に関係なく、クエリが実行されるたびに使用されます。クエリが特定の値で短絡するかどうかは、実行計画にとって重要ではありません。
私は通常、オプションのパラメーターにこれを使用します。これは短絡と同じですか?
SELECT [blah]
FROM Emp
WHERE ((@EmpID = -1) OR (@EmpID = EmpID))
これにより、-1を渡すか、属性のオプションチェックを説明するものを渡すことができます。これには、複数のテーブル、またはできればビューの結合が含まれることがあります。
非常に便利であり、dbエンジンに与える余分な作業が完全にはわかりません。
SQL Serverの場合、バージョンに依存すると思いますが、SQL Server 2000での私の経験では、@ keyがnullであっても@key = t.Keyを評価します。つまり、WHERE句を評価するときに効率的な短絡を行いません。
ユーザーがさまざまな基準を入力または入力できない柔軟なクエリを実行する方法として、あなたの例のような構造を推奨する人々を見てきました。私の観察では、@ keyがnullの場合、キーはまだクエリプランに関与しており、キーがインデックス付けされている場合、インデックスは効率的に使用されません。
さまざまな基準を持つこの種の柔軟なクエリは、おそらく動的に作成されたSQLが最適な方法の1つです。 @keyがnullの場合、クエリにまったく含めないでください。
ショートサーキットについては知りませんが、if-elseステートメントとして記述します
if (@key is null)
begin
SELECT *
FROM Table t
end
else
begin
SELECT *
FROM Table t
WHERE t.Key=@key
end
また、変数は常に方程式の右側にある必要があります。これにより、検索可能になります。
ちょうどこの質問につまずいて、すでにこのブログエントリを見つけていました: http://rusanu.com/2009/09/13/on-sql-server-boolean-operator-short-circuit/
SQLサーバーは、適切と思われる場所であればどこでもクエリを最適化することができるため、ブログの投稿で示されている例では、短絡に頼ることはできません。
ただし、CASEは書面の順序で評価するように文書化されているようです。そのブログ投稿のコメントを確認してください。
以下は、SQL Server 2008 R2での迅速で汚れたテストです。
SELECT *
FROM table
WHERE 1=0
AND (function call to complex operation)
これは、レコードなしですぐに戻ります。一種の短絡動作がありました。
次にこれを試してみました:
SELECT *
FROM table
WHERE (a field from table) < 0
AND (function call to complex operation)
レコードがこの条件を満たさないことがわかっている場合:
(a field from table) < 0
これには数秒かかりました。これは、短絡動作がもはや存在せず、すべてのレコードについて複雑な操作が評価されていることを示しています。
これがみんなを助けることを願っています。
短絡評価の主な特徴は、結果が決定されるとすぐに式の評価を停止することです。つまり、評価されるかどうかにかかわらず結果は同じになるため、式の残りの部分は無視できます。
二項ブール演算子は計算可能です:
a AND b == b AND a
a OR b == b OR a
a XOR b == b XOR a
そのため、評価の順序に関する保証はありません。評価の順序は、クエリオプティマイザーによって決定されます。
オブジェクトのある言語では、短絡評価でのみ評価できるブール式を記述できる場合があります。サンプルコードの構築は、このような言語(C#、Delphi、VB)でよく使用されます。例えば:
if(someString == null | someString.Length == 0 )
printf("no text in someString");
このC#の例は、someString == null
完全に評価されるため。短絡評価では、毎回機能します。
SQLは、初期化されていないスカラー変数(オブジェクトなし)でのみ動作するため、評価できないブール式を記述する方法はありません。 NULL値がある場合、比較はfalseを返します。
つまり、SQLでは、短絡または完全評価の使用に応じて異なる評価が行われる式を記述することはできません。
SQL実装が短絡評価を使用する場合、クエリの実行を高速化することができます。
以下は、MySQLがWHERE句の短絡を実行することを証明するデモです。
これにより、次のクエリが実行されます。
SELECT myint FROM mytable WHERE myint >= 3 OR myslowfunction('query #1', myint) = 1;
SELECT myint FROM mytable WHERE myslowfunction('query #2', myint) = 1 OR myint >= 3;
これらの唯一の違いは、OR条件のオペランドの順序です。
myslowfunction
は意図的に1秒間スリープし、実行されるたびにログテーブルにエントリを追加するという副作用があります。上記の2つのクエリを実行したときにログに記録される結果は次のとおりです。
myslowfunction called for query #1 with value 1
myslowfunction called for query #1 with value 2
myslowfunction called for query #2 with value 1
myslowfunction called for query #2 with value 2
myslowfunction called for query #2 with value 3
myslowfunction called for query #2 with value 4
上記は、OR条件が他のオペランドが常に真ではない場合(短絡のため)の左側に表示される場合、スロー関数がより多く実行されることを示しています。
これにはクエリアナライザーでさらに4秒かかるため、IFが表示されているのは短絡していません...
SET @ADate = NULL
IF (@ADate IS NOT NULL)
BEGIN
INSERT INTO #ABla VALUES (1)
(SELECT bla from a huge view)
END
保証された方法があるといいですね!