私は以下のクエリを持っています:
select databasename
from somedb.dbo.bigtable l where databasename ='someval' and source <>'kt'
and not exists(select 1 from dbo.smalltable c where c.source=l.source)
上記のクエリは3秒で完了します。
上記のクエリが値を返す場合、ストアドプロシージャをEXITにしたいので、以下のように書き直しました。
If Exists(
select databasename
from somedb.dbo.bigtable l where databasename ='someval' and source <>'kt'
and not exists(select 1 from dbo.smalltable c where c.source=l.source)
)
Begin
Raiserror('Source missing',16,1)
Return
End
ただし、これには10分かかります。
上記のクエリを次のように書き換えることもできます。これも3秒未満で完了します。
select databasename
from somedb.dbo.bigtable l where databasename ='someval' and source <>'kt'
and not exists(select 1 from dbo.smalltable c where c.source=l.source
if @@rowcount >0
Begin
Raiserror('Source missing',16,1)
Return
End
上記の書き換えの問題は、上記のクエリがより大きなストアドプロシージャの一部であり、複数の結果セットを返すことです。 C#では、各結果セットを反復処理し、いくつかの処理を行います。
上記は空の結果セットを返すため、このアプローチを使用する場合は、C#を変更して再度デプロイする必要があります。
だから私の質問は、
なぜ
IF EXISTS
計画を変更して、非常に時間がかかりますか?
以下はあなたを助けるかもしれない詳細であり、あなたが何か詳細が必要な場合は私に知らせてください:
高速実行計画
Brentozar Paste the planを使用したスロープラン
ブレントザーを使用した高速プランプランを貼り付けます
注:両方のクエリは同じ(パラメーターを使用)で、唯一の違いはEXISTS
です(ただし、匿名化中にいくつかの間違いをした可能性があります) )。
テーブル作成スクリプトは次のとおりです。
http://Pastebin.com/CgSHeqXc -小さなテーブル統計
http://Pastebin.com/GUu9KfpS -大きなテーブル統計
Paul White のブログ記事で説明されているように、 オプティマイザの内部:行の目標の詳細EXISTS
は、行の目標を導入し、_NESTED LOOPS
_または_MERGE JOIN
_ over _HASH MATCH
_
最後の例として、論理的な準結合(EXISTSで導入されたサブクエリなど)が全体的なテーマを共有していることを考慮してください。最初に一致する行をすばやく見つけるために最適化する必要があります。
クエリでは、これによりネストされたループが導入され、並列処理が削除されるため、計画が遅くなります。
したがって、クエリから_NOT EXISTS
_を使用せずにクエリを書き換える方法を見つける必要があるでしょう。
_LEFT OUTER JOIN
_を使用してクエリを書き直し、NULL
をテストすることでsmalltableに行がないことを確認することで問題が解決する場合があります
_If EXISTS(
SELECT databasename
FROM somedb.dbo.bigtable l
LEFT JOIN dbo.smalltable c ON c.source = l.source
WHERE databasename = 'someval'
AND source <> 'kt'
AND c.source IS NULL
)
_
次のように、比較する必要のあるフィールドの数に応じて、おそらくEXCEPT
クエリも使用できます。
_If EXISTS(
SELECT source
FROM somedb.dbo.bigtable l
WHERE databasename = 'someval'
AND source <> 'kt'
EXCEPT
SELECT source
FROM dbo.smalltable
)
_
あなたに注意してください Aaron Bertrand ブログ投稿 彼が存在しないことを好む理由を提供 他のアプローチがよりうまく機能するかどうかを確認し、その可能性を認識するために読む必要がありますNULL値の場合の正確性の問題。
関連するQ&A: 埋め込まれたselectステートメントよりも時間がかかる場合
私は同じ問題に遭遇しましたが、「EXISTS」の使用を避け、「COUNT()」関数と「IF ... ELSE」ステートメントを使用することで、自分自身で対処することができました。
あなたの例では、以下を試してください:
IF
(
SELECT
COUNT(l.databasename) + 1 AS databasename
FROM somedb.dbo.bigtable AS l
WHERE l.databasename ='someval'
AND l.[source] <> 'kt'
AND NOT EXISTS(SELECT 1 FROM dbo.smalltable AS c WHERE c.[source]=l.[source])
) > 1 --Acts like EXISTS
BEGIN
RAISERROR('Source missing', 16, 1)
RETURN
END
カウントに「+ 1」を追加する理由は、IF条件で "> 1"を使用できるようにするためです。 "> 0"または "<> 0"を使用すると、クエリがトリガーされ、HASHではなくネストされたループが使用されます。一致。なぜそれが正確に行われているのかを調べていないのであれば、その理由を知るのは興味深いでしょう。
お役に立てば幸いです。
明示的な結合を使用してクエリを書き換え、次のように使用する結合操作(ループ、ハッシュ、またはマージ)を指定する必要があります。
If not exists(
select databasename
from somedb.dbo.bigtable l
inner hash join dbo.smalltable c
on c.source = l.source
where databasename ='someval' and source <>'kt')
begin
Raiserror('Source missing',16,1)
Return
end
EXISTSまたはNOT EXISTSを使用すると、SQL Serverは、NESTED LOOP操作を使用してクエリプランを生成します。これは、条件を満たすすべての行を1つずつ検索して、条件を満たす最初の行を探す必要があることを前提としています。 HASH JOINを使用するとスピードアップします。