2

Common Table Expression を使用してメニューの階層パスを作成するストアド プロシージャがあります (親メニュー -> サブ メニュー -> サブ サブ メニュー -> ... のようなものを表示できます)。

それは私がそれを使いたいと思っているものにはうまく機能します.問題は、再帰CTEから取得した情報を本当に必要な情報に入れるときに発生します. データから CTE への内部結合を行い、階層パスを取得します。約 300 行を返す場合、ストアド プロシージャは平均 15 ~ 20 秒かかります。

CTE からの結果を一時テーブルに挿入し、それに基づいて結合を実行すると、手順に 1 秒もかかりません。

CTE だけを使用して参加するのになぜそんなに時間がかかるのか、または何らかの方法で CTE を誤用しているのではないかと思っていました。

**編集これは本質的にストアドプロシージャです

With Hierarchical_Path (Menu_ID, Parent_ID, Path) 

As
(
Select
    EM.Menu_Id, Parent_ID, 
            Convert(varchar(max), 
            EM.Description) as Path
From
    Menu EM
Where
--EM.Topic_No is null
    EM.Parent_ID = 0 and EM.Disabled = 0
Union All
Select  
    EM.Menu_ID,  
            EM.Parent_ID, 
            Convert(Varchar(max),E.Path + ' -> ' + EM.Description) as Path
From
    Menu EM
Inner Join
    Hierarchical_Path E
On
    EM.Parent_ID = E.Menu_ID    
)

SELECT distinct   
    EM.Description
    ,EMS.Path
FROM
    dbo.Menu em
INNER JOIN
    Hierarchical_Path EMS
ON
    EMS.Menu_ID = em.Menu_Id
    2 more INNER JOINs
    2 Left Joins
    WHERE Clause

このように (CTE に参加して) クエリを実行すると、パフォーマンスは約 20 秒です。

CTE の結果を一時テーブルに挿入して結合すると、パフォーマンスは瞬時に向上します。

クエリをもう少し分解すると、where 句に引っかかっているようです。私の質問は、CTEがいつ実行され、メモリに保存されるのかという点にあると思いますか? 一度呼び出されてメモリに残るという前提で実行していましたが、状況によっては複数回呼び出される可能性がありますか?

4

1 に答える 1

1

違いは、CTE が永続化されず、一時テーブルが (少なくともセッションに対して) 永続化されることです。永続化されていない列での結合は、既に事前評価されている一時テーブルの同じ列と比較して、SQL がデータに関する統計をまったく持たないことを意味します。基本的に、一時テーブルは使用するものをキャッシュし、SQL Server はそれをより適切に最適化できます。関数またはテーブル変数の結果を結合するときに、同じ問題が発生します。

私の推測では、一時テーブルは複数のスレッドを使用できますが、CTE 実行プランは単一のスレッドで実行を行っていると思います。これを確認するには、クエリを実行するときに実際の実行計画を含め、各演算子で反対方向を指している 2 つの水平方向の矢印を探します。それは平行性を示します。

PS - 「set statistics io on」と「set statistics time on」を設定して、実行時間に関係なくクエリを実行する実際のコストが同じかどうかを確認してください。

于 2012-05-24T22:57:15.893 に答える