web-dev-qa-db-ja.com

テーブル値関数がクエリのパフォーマンスを低下させる

今日、私が期待する方法でクエリを実行しようとする恐ろしい時間を過ごしていました。昨日クエリに存在するテーブル値関数を少し変更する必要があり、その変更によりクエリのパフォーマンスに大きな影響が出ました。実行プランを評価し、統計IOと時間を調べたところ、結果セットだけでなくテーブル変数を返すように関数を変更したため、照会されるテーブル。

私の質問は、選択/結果セットだけでなくテーブル(TableVariable)を返すようにすると、プランにそのような大きな変更が生じるのはなぜですか?

困惑...

22
scarpacci

テーブル変数を返すと、それはマルチステートメントテーブル値関数になり、それ以外はテーブルのように扱われるため、パフォーマンスが低下する可能性がありますSQL Serverが適切な実行計画の基礎となる統計情報がないため、関数は非常に少数の行を返すと推定します。より多くの行を返す場合、生成された計画は最適とは大きく異なります。

一方、SELECTのみを返すと、それはインラインテーブル値関数になります。これは、ビューと考えることができます。この場合、実際の基になるテーブルがメインクエリに取り込まれ、適切な統計に基づいてより優れた実行プランを生成できます。この場合、基本的には関数をメインクエリにマージしただけなので、実行プランには関数の記述がまったくありません。

[〜#〜] msdn [〜#〜] には、(引用)を含むCSS SQL Serverエンジニアによるすばらしい参照があります。

ただし、マルチステートメントTVFを使用すると、別のテーブルと同じように扱われます。利用可能な統計がないため、SQL Serverはいくつかの仮定を行い、概して低い見積もりを提供する必要があります。 TVFが数行しか返さない場合は問題ありません。しかし、TVFに数千の行を設定するつもりで、このTVFが他のテーブルと結合されている場合、基数の見積もりが低いために非効率的な計画になる可能性があります。

57
AdaTheDev

これは、マルチステートメントテーブルの値を持つUDFは、それが使用されている残りのSQLステートメントとインラインで処理できないため、ステートメントキャッシュプランの一部にすることはできないためです。 クエリで生成される最終結果セットのすべての行で繰り返し使用されるSQL.

Inlineテーブル値UDF otohは、それが使用されているSQLとともに処理およびコンパイルされるため、partキャッシュプランの処理およびコンパイルのみonce、生成する行の数に関係なく。

5
Charles Bretana

より多くの情報なしで決定的に答えることは本当に不可能です。しかし、私は暗闇の中でクレイジーな刺し傷をするのが好きなので。 。 。

エンジンはテーブル変数を最適化できません。エンジンは、実行計画を生成するときに、テーブル変数の行が1つだけであることを常に「想定」しています。これが、奇妙なパフォーマンスが見られる理由の1つです。

3
Phil Sandler

SQL Server 2014では、テーブル値関数データを一時テーブルに挿入してから結合することで問題を解決できました。テーブル値関数に直接結合する代わりに。

これにより、実行時間が2分から4秒に改善されました。

これが私たちのチームのために働いた例です:

-スロークエリ(2分):

DECLARE @id INT = 1;

SELECT * 
FROM [data].[someTable] T
INNER JOIN [data].[tableValueFunction](@id) TVF ON TVF.id = T.id;

-高速クエリ(4秒):

DECLARE @id INT = 1;

SELECT * 
INTO #tableValueFunction
FROM [data].[tableValueFunction](@id) TVF

SELECT * 
FROM [data].[someTable] T
INNER JOIN #tableValueFunction TVF ON TVF.id = T.id;
3
Nanuz

複数ステートメントのテーブル値UDFを使用する場合、そのUDFは、呼び出し元がその結果を使用する前に完了するまで実行されます。インラインテーブル値UDFを使用すると、SQL Serverは基本的に、UDFをマクロ展開のように呼び出し元のクエリに展開します。これには、特に以下の影響があります。

  • 呼び出しクエリのWHERE句は、インラインテーブル値UDFに直接補間できますが、マルチステートメントUDFにはできません。したがって、テーブル値UDFが、呼び出し元のクエリのWHERE句によってフィルターで除外される多くの行を生成する場合、クエリオプティマイザーはWHERE句を直接インラインテーブルに適用できます。 UDFを評価しましたが、マルチステートメントUDFにはありません。
  • SQL Serverにそのような概念がある場合、インラインテーブル値UDFはパラメーター化されたVIEWのように動作しますが、複数ステートメントのテーブル値UDFは、クエリでテーブル変数を設定して使用するように動作します。

UDFが多くの行を返し、テーブルがバッキングしている場合、これがテーブルスキャンの元になる可能性があると思います。 UDFにパラメーターをさらに追加して、呼び出し元が結果サイズを制限できるようにするか、UNIONなどの友人の助けを借りて、インラインテーブル値UDFとして再フォーマットしてみてください。結果のサイズが数行のみであることがわかっている場合およびを生成するのが難しい場合を除いて、複数ステートメントのテーブル値UDFは絶対に避けます。セットベースのロジックで必要な結果。

0
binki