これらの select クエリの間に句を含む条件で2 つのSELECT
ステートメントを記述した場合、最初のクエリが false を返した場合でも、両方のクエリが実行されますか?IF EXISTS
AND
SELECT
IF EXISTS (SELECT....) AND EXISTS(SELECT ....)
BEGIN
END
このシナリオでは、SQL Server エンジンは両方の SQL ステートメントを実行しますか?
ありがとうクリシュ
これらの select クエリの間に句を含む条件で2 つのSELECT
ステートメントを記述した場合、最初のクエリが false を返した場合でも、両方のクエリが実行されますか?IF EXISTS
AND
SELECT
IF EXISTS (SELECT....) AND EXISTS(SELECT ....)
BEGIN
END
このシナリオでは、SQL Server エンジンは両方の SQL ステートメントを実行しますか?
ありがとうクリシュ
テストを次のように書き直します
IF CASE
WHEN EXISTS (SELECT ...) THEN CASE
WHEN EXISTS (SELECT ...) THEN 1
END
END = 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 つが false の場合はゼロ行を次の。
ただし、IF
バージョンは常に1 または 0 の結果を返す必要があります。このプランは、外部結合でプローブ列を使用し、EXISTS
テストに合格しなかった場合 (単純に行を破棄するのではなく)、これを false に設定します。これは、次の Join にフィードする 1 つの行が常に存在し、常に実行されることを意味します。
バージョンにはCASE
非常に似た計画がありますが、前の条件が満たされないPASSTHRU
場合に JOIN の実行をスキップするために使用する述語を使用します。THEN
結合されたAND
s が同じアプローチを使用しない理由がわかりません。
EXISTS OR EXISTS
このEXISTS OR EXISTS
バージョンUNION ALL
では、外部準結合への内部入力として連結 ( ) 演算子が使用されていました。この配置は、最初の行が返されるとすぐに内側からの行の要求を停止できることを意味します (つまり、効果的に短絡することができます)。
/*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
*/
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
*/
すべてではないにしても、ほとんどの現代語でのIFステートメントの短絡動作に頼ることができると思います。1/0
次のように、最初に真の条件を設定し、短絡が発生しない場合にゼロ除算エラーが発生する2番目の条件を置き換えることでテストを試すことができます。
IF 1>0 OR 1/0 BEGIN
PRINT 'Short Circuited'
END
それを信用できない場合は、いつでもクエリを書き直して次のようにすることができます。
IF EXISTS(SELECT...) BEGIN
IF EXISTS(SELECT...) BEGIN
...
END
END
ANDでクエリを実行すると、それでも、両方のテーブルがアクセスされます
SET STATISTICS IO ON IF EXISTS (SELECT * from master..spt_values where [name] = 'rpcc') および EXISTS(SELECT * from master..spt_monitor where pack_sent = 5235252) PRINT 'Y'
テーブル 'spt_monitor'。スキャン カウント 1、論理読み取り 1、物理読み取り 0、先読み読み取り 0、LOB 論理読み取り 0、LOB 物理読み取り 0、LOB 先読み読み取り 0。テーブル 'spt_values'。スキャン カウント 1、論理読み取り 17、物理読み取り 0、先読み読み取り 0、LOB 論理読み取り 0、LOB 物理読み取り 0、LOB 先読み読み取り 0。
以下は、sqlteam の次のブログ エントリから引用したものです。
SQL Server が WHERE 条件の評価を短絡する方法
気が向いたときにそうしますが、すぐに思いつく方法ではありません。
開発者は、SQL Server が他のプログラミング言語で行われるような短絡を行わないこと、およびそれを強制するためにできることは何もないことに注意する必要があります。
詳細については、上記のブログ エントリの最初のリンクを確認してください。別のブログにつながっています。
最終判決は?まあ、私はまだそれを持っていませんが、特定の短絡を確実にできるのは、CASE 式で複数の WHEN 条件を表現するときだけだと言って間違いないでしょう。 標準のブール式を使用すると、オプティマイザーは、クエリを実行しているテーブル、インデックス、およびデータに基づいて、適切と思われるものを移動します。
興味深い観察がありました。tbla と tblb の 2 つのテーブルがあります。tbla には、tblb で外部キーとして使用される主キー (idvalue) があります。どちらにも idvalue = 1 の行がありますが、idvalue が -1 の行はありません。さて、以下のクエリは1つのテーブルのみを使用します
select 1
where exists
(select 1 from tbla where idvalue = -1)
and exists (select 1 from tblb where idvalue= 1)
与える
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'tbla'. Scan count 0, logical reads 3, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
オプティマイザーは、主キーと外部キーの関係があるため、値が tbla にない場合、tblb には決して存在しないことを認識しているため、これは明らかです。したがって、オプティマイザーは、tblb でのシークが不要なランタイムを決定します。
ただし、クエリを次のように書くと
select 1
where exists
(select 1 from tbla where idvalue = 1)
and exists (select 1 from tblb where idvalue= -1)
その後、両方のテーブルにアクセスします。AND 条件が満たされていることを確認するには、両方の場所をチェックする必要があることをオプティマイザが認識しているため、これは明らかです。
ただし、どちらの場合も、実際の実行計画は tbla と tblb の両方でのシークを示しています。これは私には奇妙に思えます。これについて何か考えはありますか?
これを行うことで、2 回目のスキャンを防ぐことができます。
declare @test bit
select @test = case when exists(select 1...) then 1 else 0 end
if @test = 1
begin
--1st test passed
select @test = case when exists(select 2...) then 1 else 0 end
end
if @test = 1
begin
print 'both exists passed'
end
いいえ。
SQL Server 2008 でテストしたところ、最初の評価が失敗した場合、すぐにIF
ブロックがスキップされます。
これは非常に簡単にテストできます。
最初の評価では次のようなことIF 1=0
を行い、2 番目の評価では何でもしてから、実際の実行計画を表示します。私の場合、これらの定数を評価するために定数スキャンのみを行います。