31

Mongus Pong からの質問を繰り返してい ます。一時テーブルを使用すると、入れ子になったクエリよりも高速になるのはなぜですか? 私にとって有効な答えはありません。

私たちのほとんどは、ある時点で、ネストされたクエリが特定の複雑さに達すると、パフォーマンスを維持するために一時テーブルに分割する必要があることに気付きます。これがこれまでで最も実用的な方法であり、これらのプロセスをビューにすることができなくなったことを意味するのはばかげています。多くの場合、サードパーティの BI アプリはビューでのみうまく機能するため、これは非常に重要です。

エンジンが各サブクエリを順番にスプールし、裏返しに作業するようにするための単純なクエリプラン設定が必要であると私は確信しています。サブクエリをより選択的にする方法を推測する必要はなく (非常にうまくいく場合もあります)、相関サブクエリの可能性もありません。プログラマーが括弧内の自己完結型コードによって返されることを意図したデータのスタック。

単純にサブクエリから #table に変更するだけで 120 秒から 5 秒かかることがよくあります。基本的に、オプティマイザはどこかで大きな間違いを犯しています。確かに、オプティマイザーがテーブルを正しい順序で参照できるようにするには、非常に時間のかかる方法があるかもしれませんが、それでも保証はありません。ここで理想的な 2 秒の実行時間を求めているのではなく、ビューの柔軟性の範囲内で一時テーブルが提供する速度を求めているだけです。

私はこれまでここに投稿したことはありませんでしたが、私は何年も SQL を書いており、この問題を受け入れるようになったばかりの他の経験豊富な人々のコメントを読みました。特別なヒントは X...

4

4 に答える 4

14

この動作が見られる理由については、いくつかの説明が考えられます。いくつかの一般的なものは

  1. サブクエリまたは CTE が繰り返し再評価されている可能性があります。
  2. 部分的な結果をテーブルにマテリアライズ#tempすると、式からいくつかの可能なオプションが削除されるため、プランのその部分の結合順序がより最適になる場合があります。
  3. 部分的な結果を#tempテーブルに具体化すると、不十分なカーディナリティの見積もりが修正されるため、計画の残りの部分が改善される場合があります。

最も信頼できる方法は、単純に#tempテーブルを使用して自分で具体化することです。

ポイント1に関して失敗した場合は、 CTEまたは派生テーブルの中間マテリアライゼーションを強制するためのヒントを提供する を参照してください。を使用するとTOP(large_number) ... ORDER BY、繰り返し再評価するのではなく、結果をスプールすることが促進されることがよくあります。

それが機能しても、スプールに統計はありません。

ポイント 2 と 3 については、目的の計画が得られなかった理由を分析する必要があります。sargable述語を使用するようにクエリを書き直すか、統計を更新すると、より良い計画が得られる可能性があります。クエリ ヒントを使用して目的のプランを取得できなかった場合。

于 2013-09-12T14:13:42.340 に答える
5

各サブクエリを順番にスプールするようにエンジンに指示するクエリ ヒントがあるとは思えません。

OPTION (FORCE ORDER)エンジンが指定された順序で JOIN を実行するように強制するクエリ ヒントがあります。このヒントにより、複雑なクエリに対してより効率的なプランが得られることがあり、エンジンは次善のプランを主張し続けます。もちろん、オプティマイザは通常、最善の計画を決定するために信頼されるべきです。

理想的には、CTE またはサブクエリを「マテリアライズド」または「匿名一時テーブル」として指定できるクエリ ヒントが存在しますが、存在しません。

于 2013-09-12T14:31:16.917 に答える
3

もう 1 つのオプション (この記事の今後の読者向け) は、ユーザー定義関数を使用することです。マルチステートメント関数 (ストアド プロシージャ間でデータを共有する方法で説明されている) は、SQL Server にサブクエリの結果を強制的に実体化させるように見えます。さらに、結果のテーブルに主キーとインデックスを指定して、クエリ オプティマイザーを支援することができます。この関数は、ビューの一部として select ステートメントで使用できます。例えば:

CREATE FUNCTION SalesByStore (@storeid varchar(30))
   RETURNS @t TABLE (title varchar(80) NOT NULL PRIMARY KEY,
                     qty   smallint    NOT NULL)  AS
BEGIN
   INSERT @t (title, qty)
      SELECT t.title, s.qty
      FROM   sales s
      JOIN   titles t ON t.title_id = s.title_id
      WHERE  s.stor_id = @storeid
   RETURN
END

CREATE VIEW SalesData As
SELECT * FROM SalesByStore('6380')
于 2014-09-29T14:32:24.147 に答える