2

関数の結果に基づいてフィルタリングするストアドプロシージャがありDATEADDます-SQLサーバーはその関数の出力に基づいて統計を保存できないため、ユーザー定義関数の使用に似ていると理解しています。実行計画。

クエリは次のようになります。

SELECT /* Columns */ FROM
TableA JOIN TableB
ON TableA.id = TableB.join_id
WHERE DATEADD(hour, TableB.HoursDifferent, TableA.StartDate) <= @Now

(したがって、結果を事前に計算することはできませんDATEADD

私が見ているのは、ひどいひどい実行プランです。これは、SQLサーバーがツリーの一部から返される行数を実際には約65,000であるのに、1と誤って推定しているためだと思います。ただし、データベースに異なる(必ずしも少ないわけではない)データが存在する場合、同じストアドプロシージャがほんのわずかな時間で実行されるのを見てきました。

私の質問は-このような場合、クエリオプティマイザーは関数の結果をどのように推定しますか?

更新:参考までに、私は、なぜあるときは良い実行計画を取得し、残りの時間は取得しないのかを理解することにもっと興味があります-私はこれをどのように修正するかについてすでにかなり良い考えを持っています長期的には。

4

3 に答える 3

3

ここで問題となるのは、計画のコストではありません。列の関数は、SQLがインデックスシークを実行するのを防ぎます。インデックススキャンまたはテーブルスキャンを取得します。

私が提案するのは、関数から列の1つを取得できるかどうかを確認することです。基本的には、関数を等式の反対側に移動できるかどうかを確認します。完全ではありませんが、少なくとも1つの列をインデックスシークに使用できることを意味します。

TableB.HoursDifferenceのインデックス、次にTableAの結合列のインデックスを持つこのようなもの(大まかなアイデア、テストされていません)

DATEDIFF(hour, @Now, TableA.StartDate) >= TableB.HoursDifferent

コスト面では、統計を使用して正確な見積もりを取得できず、不等式であるため、オプティマイザーはテーブル「thumb-suck」の30%を使用すると思われます。つまり、テーブルの30%がその述語によって返されると推測されます。

実行計画を見ずに確実に言うのは本当に難しいです。あなたは1行の見積もりと65000の実際について言及します。場合によっては、それはまったく問題ではありません。 http://sqlinthewild.co.za/index.php/2009/09/22/estimated-rows-actual-rows-and-execution-count/

于 2009-09-25T14:38:07.347 に答える
1

関数を確認することは役立ちますが、私が見た1つのことは、そのような関数をクエリに埋め込むと、パフォーマンスが低下する可能性があることです。あなたがそれのいくつかを前もって評価することができれば、あなたはより良い状態にあるかもしれません。たとえば、代わりに

WHERE MyDate < GETDATE()

試す

DECLARE @Today DATETIME
SET @Today = GETDATE()
...
WHERE MyDate < @Today

これはパフォーマンスが良いようです

于 2009-09-25T14:10:58.467 に答える
1

@ Kragen、

簡単な答え: 10個のテーブルでクエリを実行している場合は、それに慣れてください。クエリのヒントについてすべてを学ぶ必要があります。さらに、さらに多くのトリックを学ぶ必要があります。

長い答え:

SQL Serverは通常、最大約3〜5個のテーブルに対してのみ優れたクエリプランを生成します。私の経験では、それを超えると、基本的に、すべてのインデックスと結合のヒントを使用して、クエリプランを自分で作成する必要があります。(さらに、スカラー関数はCost = Zeroで推定されるようですが、これは非常に狂っています。)

その理由は、その後は非常に複雑すぎるからです。クエリオプティマイザーはアルゴリズムで何をするかを決定する必要があり、SQL Serverチームの最も優秀な天才でさえ、真に普遍的に機能するアルゴリズムを作成するには、可能な組み合わせが多すぎます。

彼らはオプティマイザーがあなたより賢いと言います。それは本当かもしれません。しかし、1つの利点があります。その利点は、それが機能しない場合、それを捨てて再試行できることです!データを知っていれば、6回目の試行までに、10テーブルの結合であっても、許容できるものが得られるはずです。クエリオプティマイザーはそれを行うことができず、ある種の計画を即座に考え出す必要があり、2度目のチャンスはありません。

私のお気に入りのトリックは、where句をcaseステートメントに変換して、where句の順序を強制することです。それ以外の:

WHERE
predicate1
AND predicate2
AND....

これを使って:

WHERE
case 
when not predicate1 then 0
when not predicate2 then 0
when not .... then 0
else 1 end = 1

述語を最も安いものから最も高いものの順に並べると、論理的には同じですが、SQLサーバーが混乱することのない結果が得られます。つまり、指定した順序で述語を実行する必要があります。

于 2011-03-02T22:54:48.527 に答える