web-dev-qa-db-ja.com

テーブル値関数制限パフォーマンスを使用したCROSS APPLY

パラメータ化されたテーブル値関数のCROSS APPLYに問題があります。簡略化した疑似コードの例を次に示します。

SELECT * 
FROM (
    SELECT lor.*
    FROM LOT_OF_ROWS_TABLE lor
    WHERE ...
) AS lor
CROSS APPLY dbo.HeavyTableValuedFunction(lor.ID) AS htvf
INNER JOIN ANOTHER_TABLE AS at ON lor.ID = at.ID 
WHERE ...
  • テーブルLOT_OF_ROWS_TABLEの内部選択が多くの行を返しています。
  • テーブルLOT_OF_ROWS_TABLEANOTHER_TABLEを結合すると、1行または数行のみが返されます。
  • テーブル値関数は非常に時間がかかり、多くの行を呼び出す場合、選択は非常に長く続きます。

私の問題:

関数は、LOT_OF_ROWS_TABLEを結合するだけでデータが制限されるという事実に関係なく、ANOTHER_TABLEから返されるすべての行に対して呼び出されます。

選択は表示された形式である必要があります-生成されますが、実際にははるかに困難です。

それを書き直そうとすると、非常に高速になる可能性がありますが、次のように書き換えることはできません。

SELECT * 
FROM (
    SELECT lor.*
    FROM LOT_OF_ROWS_TABLE lor
    WHERE ...
) AS lor
INNER JOIN ANOTHER_TABLE AS at ON lor.ID = at.ID 
CROSS APPLY dbo.HeavyTableValuedFunction(at.ID) AS htvf
WHERE ...

知りたい:

最終的に制限された行に対してのみselectが関数を呼び出すように強制する設定やヒントなどはありますか?

ありがとうございました。

編集:

テーブル値関数は非常に複雑です: http://Pastebin.com/w6azRvxR 。私たちが話している選択は「ユーザー設定」であり、生成されます: http://Pastebin.com/bFbanY2n

9
Pavel Hodek

このクエリを2つの部分に分割できます。テーブル変数または一時テーブルを使用します。

SELECT lor.*,at.* into #tempresult
FROM (
    SELECT lor.*
    FROM LOT_OF_ROWS_TABLE lor
    WHERE ...
) lor
INNER JOIN ANOTHER_TABLE AS at ON lor.ID = at.ID 
WHERE ...

時間のかかる部分であるテーブル値関数を実行する

SELECT  * FROM #tempresult
CROSS APPLY dbo.HeavyTableValuedFunction(#tempresult.ID) AS htvf
2
Sankara

これはあなたが探しているものだと思います。

プラン強制シナリオ:書き換えられたクエリから取得したプランを強制するプランガイドを作成する

基本的には、正しい結合順序を使用して生成されたプランを取得するためのクエリの書き直しについて説明しています。次に、その計画を保存し、保存した計画を使用するように既存のクエリ(変更されない)を強制します。

私が入れたBOLリンクは、結合を別の順序で入れ、FORCE ORDERヒント。次に、sp_create_plan_guild再作成されたクエリからプランを取得して、元のクエリで使用します。

1
Kenneth Fisher

YESとNO ...結果を比較するために、サンプルデータINと結果OUTなしで達成しようとしていることを解釈することは困難です。

知りたい:

最終的に制限された行に対してのみselectが関数を呼び出すように強制する設定やヒントなどはありますか?

だから私は直接あなたの質問に答えます(3年後!!)、直接の声明で:

CTE、およびCROSS APPLYとINNER JOINの違い、およびケースでCROSS APPLYを使用する必要がある理由を理解する必要があります。関数のコードを「取り得」、CTEを使用して単一のSQLステートメントに適用できます。

つまり:

this および this をお読みください。

基本的に、このようなもの...

WITH    t2o AS
        (
        SELECT  t2.*, ROW_NUMBER() OVER (PARTITION BY t1_id ORDER BY rank) AS rn
        FROM    t2
        )
SELECT  t1.*, t2o.*
FROM    t1
INNER JOIN
        t2o
ON      t2o.t1_id = t1.id
        AND t2o.rn <= 3

クエリを適用してONCEが必要な日付を推定し、CTEを使用してから、CROSS APPLYを使用して2番目のSQLを適用します。

あなたは選択肢がありません。 ONE SQLでやろうとしていることはできません。

0
Fandango68