3

親子関係のある行を持つテーブルのクエリで問題が発生しました。簡単な例を考えてみると、stackexchange スキーマが非常に似ていることに気付きました。

それで、stackexchange データ エクスプローラーを介して、stackoverflow の投稿テーブルにクエリを実行していると想像してください。すべての投稿とそれに関連する回答のサブセットを取得しようとしています。

サンプル クエリについては、 https://data.stackexchange.com/stackoverflow/query/121981/a-subset-of-questions-and-associated-answersを参照してください。

投稿のサブセットは、かなり複雑でコストのかかるクエリ プランを持つビューで定義されます。以下の例では、単純に上の 2 つの行を選択するように簡略化されています。

ユニオンを使用する最初の方法:

with ExpensiveView as (select top 2 ID from Posts order by ID)

select Posts.*
from ExpensiveView
left outer join Posts
  ON ExpensiveView.Id = Posts.Id 
  
union all

select Posts.*
from ExpensiveView
left outer join Posts
  ON ExpensiveView.Id = Posts.ParentId

ExpensiveViewは 2 回評価されるため、この方法は避けたいと思います。上記の単純化されたバージョンでは明らかに問題ではありませんが、もう 1 つの複雑なバージョンでは問題が発生します。

2 番目の方法では、条件付き結合句で単一の選択を使用します。

with ExpensiveView as (select top 2 ID from Posts order by ID)

select Posts.*
from ExpensiveView
left outer join Posts
  ON ExpensiveView.Id = Posts.Id or ExpensiveView.Id = Posts.ParentId

これにより、2 回評価されることは回避ExpensiveViewされますが、途方もなく大きなクラスター化インデックス スキャンが発生します。IDごとにインデックス全体をスキャンしているようですExpensiveView(つまり、2 * 14977623 =〜3000万行)。これは非常に遅いです。

2 つの質問

2 番目のクエリの条件付き結合により、インデックス スキャンがこれほど大規模になるのはなぜですか?

ExpensiveView何度も評価されることなく、探している結果を得る方法はありますか?

4

1 に答える 1

0

これを試して

with
ExpensiveView as (select top 2 ID from Posts order by ID),
CTE_Posts as (
    select *, NP.Id as New_Post_ID
    from Posts as P
        outer apply (select P.Id union all select P.ParentId) as NP
)
select
    P.*
from ExpensiveView as E
    left outer join CTE_Posts as P on P.New_Post_ID = E.ID
于 2013-07-27T20:16:00.517 に答える