2011-02-04 6 views
1

SQL Server 2005を使用していて、IN句内でサブクエリを使用しているときにいくつかの結果をフィルタリングするときに奇妙なことに気付きました。例えば、これは私の現在のクエリであり、それは平均で70秒で実行されます:私は2つの別々のクエリにそれらを打破する場合IN(サブクエリ)を使用するとパフォーマンスが大幅に低下します。どうして?

select Phone, ZipCode, sum(Calls) as Calls, sum(Sales) as Sales 
from Archive 
where CustomerID = 20 
and ReportDate = '2/3/2011' 
and Phone in (
    select Phone 
    from PlanDetails 
    where Phone is not null 
    and Length is not null 
    and PlannedImp > 0 
    and CustomerID = 20 
    and (StatusID <> 2 and StatusID <> 7) 
    and SubcategoryID = 88 
) 
group by Phone, ZipCode 

はしかし、彼らが実行するために1秒ごとの下で取ります。

select Phone 
from PlanDetails 
where Phone is not null 
and Length is not null 
and PlannedImp > 0 
and CustomerID = 20 
and (StatusID <> 2 and StatusID <> 7) 
and SubcategoryID = 88 

と最後に

select Phone, ZipCode, sum(Calls) as Calls, sum(Sales) as Sales 
from Archive 
where CustomerID = 20 
and ReportDate = '2/3/2011' 
group by Phone, ZipCode 

、私はこれが最初のクエリと同じ結果を返しますが、およそ2〜3秒で行う場合:過去数について

select Phone 
into #tempTable 
from PlanDetails 
where Phone is not null 
and Length is not null 
and PlannedImp > 0 
and CustomerID = 20 
and (StatusID <> 2 and StatusID <> 7) 
and SubcategoryID = 88 

select Phone, ZipCode, sum(Calls) as Calls, sum(Sales) as Sales 
from Archive 
where CustomerID = 20 
and ReportDate = '2/3/2011' 
and Phone in (
    select Phone 
    from #tempTable 
) 
group by Phone, ZipCode 

私はこのクエリが遅いのではなく、IN句の中で(やや複雑な)サブクエリを使用するクエリがパフォーマンスを壊すだけであることに気づいています。その理由は何ですか?

これらのクエリで使用できるインデックスは、両方のテーブルのCustomerIDの非クラスタ化インデックスのみです。私は、低速クエリと高速クエリの両方の実行計画を見て、非クラスタ化インデックスがArchiveテーブルを検索することは、コストの最も高いパーセンテージ(80〜90%)を占めることを確認しました。しかし、唯一の違いは、低速クエリのステップではCPUコストが7.1で、高速クエリではCPUコストが1.7であることです。

+0

この種の不具合(間違ったクエリプラン)が発生すると、sp_updatestatsを実行すると問題が解決することがあります。 – Magnus

答えて

4

これはデータベースシステム、バージョン、設定などによって異なりますが、一般的には、その内部クエリをキャッシュするためにデータベースが失敗する(または拒否される)ため、実行されています毎回外側のクエリあなたは問題をO(n)効率クラスからO(n^2)に変更しています。

+0

これはまさに起こっていたことです。グラフィカルなプランではなく、データプランとして実行プランを見た後、サブクエリメソッドがより多くの実行(〜326000)と内部結合(〜14244)を表示していたことに気付きました。 – Jason

2

引用IN vs. JOIN vs. EXISTS

我々は今、世論に反しを参照して、IN /クエリは、SQL ServerでのJOINクエリよりも少ない効率的ではありませんが存在します。

実際、JOINは2つのステップでこれら2つの操作を実行する必要がありますが、Semi Joinメソッドは1つのハッシュテーブルに対して集計と一致を許可するため、非インデックステーブルではJOINクエリの効率が低下します。

さらに、索引付けおよび現在の表統計の方法は、オプティマイザが問合せの実行を決定する方法に大きな役割を果たします。

2

クエリを結合で書き直すとどうなりますか? EXISTS代わりINのを使用してクエリを書き直す
1.試してみてください。

select a.Phone, a.ZipCode, sum(a.Calls) as Calls, sum(a.Sales) as Sales 
from Archive a 
    inner join PlanDetails pd 
     on a.CustomerID = pd.CustomerID 
      and a.Phone = pd.Phone 
where a.CustomerID = 20 
    and a.ReportDate = '2/3/2011' 
    and pd.Length is not null 
    and pd.PlannedImp > 0 
    and (pd.StatusID <> 2 and pd.StatusID <> 7) 
    and pd.SubcategoryID = 88 
group by a.Phone, a.ZipCode 
+0

これは速く(<2秒)私は実際にIN(サブクエリ)がすべてを邪魔している理由を理解しようとしているので、将来どのような種類のクエリを避けるべきかを知っています。 – Jason

2

私は2つのソリューションを提案したいです。これは、古いSQL Serverのバージョンを使用すると役に立ちます(SQL Server 2005 EXITSTINが異なる実行計画を生成する前に、私のメモリがうまく機能している場合)。
2. INNER JOINを使用してみてください(あなたはまた、CTEを使用することができます):個人的に

select Phone, ZipCode, sum(Calls) as Calls, sum(Sales) as Sales 
from Archive 
INNER JOIN 
(
    select DISTINCT Phone // DISTINCT to avoid duplicates 
    from PlanDetails 
    where Phone is not null 
    and Length is not null 
    and PlannedImp > 0 
    and CustomerID = 20 
    and (StatusID <> 2 and StatusID <> 7) 
    and SubcategoryID = 88 
)XX ON (XX.Phone = Archive.Phone) 
where CustomerID = 20 and ReportDate = '2/3/2011'  
group by Phone, ZipCode 

、私は第二のアプローチは、あなたに多くの、より良い結果を与えることを期待したいです。

+0

奇妙なことに、これには最初のクエリとほぼ同じ時間がかかります。私はそのことを、サブクエリ内のPlanDetailsから選択して、すべてを邪魔しているという事実だと思います。 – Jason

+0

@Jason EXISTSクエリはどのように実行されますか? – Magnus

関連する問題