テストを次のように書き直します
IF CASE
WHEN EXISTS (SELECT ...) THEN CASE
WHEN EXISTS (SELECT ...) THEN 1
END
END = 1
これにより、ここで説明するように短絡が保証されますが、オプティマイザーに任せるのではなく、事前に評価するために最も安価なものを選択する必要があることを意味します。
以下の非常に限られたテストでは、テスト時に次のことが当てはまるようでした
1。 EXISTS AND EXISTS
EXISTS AND EXISTS
バージョンが最も問題があるようです。これにより、いくつかの外側の半結合がチェーンされます。いずれの場合も、最初に安価なテストを実行するためにテストの順序を再調整しませんでした(このブログ投稿の後半で説明されている問題)。 IF ...
で バージョンは短絡していなかったので、それがあったとしても何の違いもありませんでした。ただし、この結合された述語がWHERE
に配置された場合 条項は計画が変更され、実行 再配置が有益であった可能性があるように短絡します。
/*All tests are testing "If False And False"*/
IF EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)=2)
AND EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)=1)
PRINT 'Y'
/*
Table 'spt_values'. Scan count 1, logical reads 9
Table 'spt_monitor'. Scan count 1, logical reads 1
*/
IF EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)=1)
AND EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)=2)
PRINT 'Y'
/*
Table 'spt_monitor'. Scan count 1, logical reads 1
Table 'spt_values'. Scan count 1, logical reads 9
*/
SELECT 1
WHERE EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)=2)
AND EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)=1)
/*
Table 'Worktable'. Scan count 0, logical reads 0
Table 'spt_monitor'. Scan count 1, logical reads 1
*/
SELECT 1
WHERE EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)=1)
AND EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)=2)
/*
Table 'Worktable'. Scan count 0, logical reads 0
Table 'spt_values'. Scan count 1, logical reads 9
*/
これらすべての計画は非常に似ているように見えます。 SELECT 1 WHERE ...
の動作が異なる理由 バージョンとIF ...
前者の場合、条件がfalseの場合、正しい動作は結果を返さないため、OUTER SEMI JOINS
をチェーンするだけです。 1つが偽の場合、ゼロ行は次の行に繰り越されます。
ただし、IF
バージョン常に 1またはゼロの結果を返す必要があります。このプランは、外部結合でプローブ列を使用し、EXISTS
の場合、これをfalseに設定します テストに合格しません(単に行を破棄するのではありません)。これは、次の結合に常に1つの行がフィードされ、常に実行されることを意味します。
CASE
バージョンの計画は非常に似ていますが、PASSTHRU
を使用しています 前のTHEN
の場合、JOINの実行をスキップするために使用する述語 条件が満たされていません。 AND
を組み合わせた理由がわかりません sは同じアプローチを使用しません。
2。 EXISTS OR EXISTS
EXISTS OR EXISTS
バージョンは連結を使用しました(UNION ALL
)外部半結合への内部入力としての演算子。この配置は、最初の行が返されるとすぐに内側からの行の要求を停止できることを意味します(つまり、効果的に短絡できます)。4つのクエリはすべて、より安価な述語が最初に評価された同じ計画になりました。
/*All tests are testing "If True Or True"*/
IF EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)=1)
OR EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)<>1)
PRINT 'Y'
/*
Table 'Worktable'. Scan count 0, logical reads 0
Table 'spt_monitor'. Scan count 1, logical reads 1
*/
IF EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)<>1)
OR EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)= 1)
PRINT 'Y'
/*
Table 'Worktable'. Scan count 0, logical reads 0
Table 'spt_monitor'. Scan count 1, logical reads 1
*/
SELECT 1
WHERE EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)= 1)
OR EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)<>1)
/*
Table 'Worktable'. Scan count 0, logical reads 0
Table 'spt_monitor'. Scan count 1, logical reads 1
*/
SELECT 1
WHERE EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)<>1)
OR EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)=1)
/*
Table 'Worktable'. Scan count 0, logical reads 0
Table 'spt_monitor'. Scan count 1, logical reads 1
*/
3。 ELSE
を追加する
AND
を変換するためにドモルガンの法則を試してみることが私には思い浮かびました。 OR
へ それが何か違いを生んだかどうかを確認してください。最初のクエリを変換すると
IF NOT ((NOT EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)=2)
OR NOT EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)=1)))
PRINT 'Y'
ELSE
PRINT 'N'
/*
Table 'spt_monitor'. Scan count 1, logical reads 1
Table 'spt_values'. Scan count 1, logical reads 9
*/
したがって、これでも短絡動作に違いはありません。ただし、NOT
を削除した場合 IF ... ELSE
の順序を逆にします 現在行う条件 短絡!
IF (NOT EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)=2)
OR NOT EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)=1))
PRINT 'N'
ELSE
PRINT 'Y'
/*
Table 'Worktable'. Scan count 0, logical reads 0
Table 'spt_monitor'. Scan count 1, logical reads 1
*/