[FIX] 外部結合およびフィルタ選択条件を結果の大きな結合および外部結合の前に実行する

現象

外部結合の内部テーブル (左外部結合の右テーブル、右外部結合の左テーブルなど) の WHERE 句にフィルタ選択条件を使用する外部結合を少なくとも 1 つ含むクエリを実行すると、SQL Server は、先に外部結合を実行してからフィルタ選択条件を適用するという手順ではなく、最初に結果の大きな結合を実行することがあります。外部結合のフィルタ選択条件が、より結果の小さなクエリ条件であった場合、プランでこの条件を先に処理しないと、以下の現象が発生することがあります。
  • 中間結合サイズが大きくなる。
  • SQL Server プロセスのリソース使用率が高くなる。
  • クエリに対する応答時間が長くなる。

解決方法

この問題を解決するには、Microsoft SQL Server 2000 の最新の Service Pack を入手します。関連情報を参照するには、以下の「サポート技術情報」 (Microsoft Knowledge Base) をクリックしてください。
290211
最新の SQL Server 2000 Service Pack の入手方法
: 次の修正プログラムは Microsoft SQL Server 2000 Service Pack 3 がリリースされる前に作成されました。


修正プログラム (英語版) の属性は次のとおりです。ただし、これより新しい修正プログラムがリリースされている可能性もあります。

バージョン ファイル名
-----------------------------

8.00.0584 Sqlservr.exe
: ファイルの依存関係のため、上記のファイルを含む最新の修正プログラムや機能には、追加ファイルも含まれる可能性があります。


状況

マイクロソフトでは、この問題をこの資料の冒頭に記載したマイクロソフト製品の問題として認識しています。

この問題は、Microsoft SQL Server 2000 Service Pack 3 で最初に修正されました。

詳細

以下の例では、上記の結合について説明するために、pubs データベースを使用しています。
set ansi_nulls off
go

use pubs
go

create procedure dbo.ansi_nulls_param @P1 varchar(11) as
select t.title_id, a.au_id, ta.title_id, s.stor_id from titles t
left outer join titleauthor ta on ta.title_id = t.title_id
inner join authors a on a.au_id = t.title_id
inner join sales s on s.title_id = t.title_id
where ta.title_id = @P1
go

exec dbo.ansi_nulls_param '123-45-6789'
go

drop proc dbo.ansi_nulls_param
go

--Slower Query Plan:
|--Filter(WHERE:([ta].[title_id]=[@P1]))
|--Nested Loops(Left Outer Join, OUTER REFERENCES:([t].[title_id]))
|--Nested Loops(Inner Join, OUTER REFERENCES:([s].[title_id]))
| |--Nested Loops(Inner Join, OUTER REFERENCES:([s].[title_id]))
| | |--Index Scan(OBJECT:([pubs].[dbo].[sales].[titleidind] AS [s]))
| | |--Clustered Index Seek(OBJECT:([pubs].[dbo].[authors].[UPKCL_auidind] AS [a]), SEEK:([a].[au_id]=[s].[title_id]) ORDERED FORWARD)
| |--Clustered Index Seek(OBJECT:([pubs].[dbo].[titles].[UPKCL_titleidind] AS [t]), SEEK:([t].[title_id]=[s].[title_id]) ORDERED FORWARD)
|--Index Seek(OBJECT:([pubs].[dbo].[titleauthor].[titleidind] AS [ta]), SEEK:([ta].[title_id]=[t].[title_id]) ORDERED FORWARD)

--Faster Query Plan:
|--Nested Loops(Inner Join, OUTER REFERENCES:([a].[au_id]))
|--Nested Loops(Inner Join, OUTER REFERENCES:([t].[title_id]))
| |--Filter(WHERE:([ta].[title_id]=[@P1]))
| | |--Nested Loops(Left Outer Join, OUTER REFERENCES:([t].[title_id]))
| | |--Index Scan(OBJECT:([pubs].[dbo].[titles].[titleind] AS [t]))
| | |--Index Seek(OBJECT:([pubs].[dbo].[titleauthor].[titleidind] AS [ta]), SEEK:([ta].[title_id]=[t].[title_id]) ORDERED FORWARD)
| |--Clustered Index Seek(OBJECT:([pubs].[dbo].[authors].[UPKCL_auidind] AS [a]), SEEK:([a].[au_id]=[t].[title_id]) ORDERED FORWARD)
|--Index Seek(OBJECT:([pubs].[dbo].[sales].[titleidind] AS [s]), SEEK:([s].[title_id]=[a].[au_id]) ORDERED FORWARD)
titleauthor テーブルは左外部結合の右テーブルであり、titleauthor テーブルの WHERE 句の条件は外部結合の後に適用されます。最初の出力では、低速なクエリ プランが表示されています。このクエリ プランでは、最初にすべての内部結合が実行され、最後に外部結合とフィルタ選択が実行されます。フィルタ選択がこのクエリで最も結果が小さくなる条件であっても、この順序は変わりません。2 番目のクエリ プランは高速のプランを示すために作成したものです。高速なプランでは外部結合とフィルタ選択が実行された後、内部結合が実行されます。


この例の場合、修正プログラムの適用後もオプティマイザは最初のプランを選択します。これは、テーブルが非常に小さいので最初のプランの見積もりコストが低く、その後の選択肢を探すよりも最初のプランを実行する方が効率が良いと判断されるからです。テーブル内のデータが増えると最初のプランのコストが高くなり、オプティマイザは 2 番目のプランを選択するようになります。
プロパティ

文書番号:318530 - 最終更新日: 2008/07/11 - リビジョン: 1

フィードバック