web-dev-qa-db-ja.com

Dodgy T-SQL Query Executionは私を狂わせます

The Execution Plan SQL Server2019。これは、xmlプランを使用した Gist へのリンクです。

こんにちは、含まれているステータスの1つを持つレコードが見つからない場合に、このクエリの実行に0.02秒かかる理由を理解するのに時間がかかります。含まれているステータスの1つを持つレコードが見つかると、はるかに速くなる傾向があります。これは、一致する行が1つ見つかるとクエリが停止するためだと思います。

SELECT TOP 1 IDNum,
             FORMAT(Date, 'M/d/yy') AS theDate,
             Status,
             Rate
FROM   theDB
       INNER JOIN DomainTable
               ON theDB.IDNum = DomainTable.IDNum
WHERE  DomainIP = '127.0.0.1'
       AND status IN ( 'Active', 'To ReActivate', 'To Deactivate', 'Deactivate ASAP',
                       'SUSPENDED', 'SUSPENDED X', 'SUSPENDED Y', 'SUSPENDED Z' )
ORDER  BY theDB.IDNum DESC 

実行計画では、最大コストは33%のTOP N SORTです29%にクラスター化されたインデックスシークがあります29%を使用しているDomainTableにキールックアップがありますDomainTableのIPのインデックスシークは9%です

私の質問は:

tOP Nがそれほど重くないようにする方法はありますか?

0.02秒はそれほど遅くはありませんが、このクエリもかなり軽量です。できるだけ最適化したいと思います。

8
ZCT

tOP Nがそれほど重くないようにする方法はありますか?

実行計画のしくみについて、ここでは少し誤解があると思います。

その数は、推定コストにすぎません。これは、SQL Serverが最も効率的な実行プランを決定するために使用するモデルです。これは実行時に更新されないため、オペレーターが使用するリソースがほとんどない場合でも、計画が作成されたときと同じように見積もりコストが表示されます。

提供したランタイムプランを見ると、Sentry One Plan Explorerを使用すると、「高価な」演算子の一部がまったく実行されなかった(「グレー表示」されている)ことが少しわかりやすくなります。

enter image description here

このクエリが実行に0.02秒かかる理由を理解するのに時間がかかっています。

ここでどのように時間を測定しているのかわかりませんが、実行計画では、クエリ全体の実行に1ミリ秒未満、コンパイルに9ミリ秒かかったことが示されています。プランXMLからの抜粋を参照してください。

<QueryPlan DegreeOfParallelism="1" MemoryGrant="1024" CachedPlanSize="48" CompileTime="9" CompileCPU="9" CompileMemory="736">
...
<QueryTimeStats CpuTime="0" ElapsedTime="0" />

Maxには、クエリの全般的な改善に関するいくつかの良い提案があります。間違いなく それらをチェックしてください です。しかし、これも指摘する価値があると思いました。

インデックス付きビュー を使用してTopNソートを排除できる可能性がありますが、現時点でオーバーヘッドの価値があるかどうかはわかりません。

12
Josh Darnell

次のインデックスを作成してみてください(私は、インデックス名の命名規則に似たものを使用しています):

_CREATE INDEX DomainTable$DomainIP$IDNum
ON dbo.DomainTable (DomainIP, IDNum DESC);

CREATE INDEX theDB$idx001
ON dbo.theDB (IDNum DESC)
INCLUDE (theDate, [Status], Rate);

_

最初のインデックスにより、SQL ServerはDomainIPでシークを実行できます。これは非常に高速で、IDNum列を降順で含みます。これにより、クエリプランからのキールックアップ操作が不要になり、必要な降順のソートが不要になります。

2番目のインデックスを使用すると、SQL Serverで並べ替えを排除できる場合があります。クエリの実行に役立つか、妨げられるかを確認してください。 最小限、完全、検証可能な例 を質問に追加すると、それをテストすることができます。

とにかく、クエリに対する次の変更を検討してください。

_IF OBJECT_ID(N'tempdb..#statii', N'U') IS NOT NULL
BEGIN
    DROP TABLE #statii;
END
CREATE TABLE #statii 
(
    [status] varchar(20) NOT NULL
        PRIMARY KEY CLUSTERED
);

INSERT INTO #statii ([status])
VALUES ('Active')
    , ('To ReActivate')
    , ('To Deactivate')
    , ('Deactivate ASAP')
    , ('SUSPENDED')
    , ('SUSPENDED X')
    , ('SUSPENDED Y')
    , ('SUSPENDED Z');

SELECT TOP 1 theDB.IDNum
    , CONVERT(varchar(30), theDB.theDate, 22)
    , theDB.[Status]
    , theDB.Rate 
FROM theDB 
    INNER JOIN DomainTable ON theDB.IDNum = DomainTable.IDNum 
    INNER JOIN #statii s ON theDB.[status] = s.[status]
WHERE DomainTable.DomainIP = '127.0.0.1' 
ORDER BY theDB.IDNum DESC;
_

注意すべき点がいくつかあります。

  1. WHERE ... IN (...)句を使用する代わりに、これらの値を一時テーブルに挿入して結合します。この方法でパフォーマンスが大幅に向上していることに気付かない場合がありますが、IN (...)句の項目のリストが大きくなるにつれて、パフォーマンスの違いがますます顕著になります。パフォーマンスが低下することが判明した場合は、IN (...)句に戻すことができます。テストする価値があると思います。

  2. FORMATは使用しないでください-遅いです。可能な場合はCONVERTまたはCASTを使用してください。

  3. _#statii_テーブルで行ったように、テーブルエイリアスを使用します。

  4. キーワードを大文字にし、データ型を小文字のままにします。

  5. 決して 1keywords を列名として使用します。 status、あなたを見ています。その列はBlahStatusという名前にする必要があります。ここで、Blahはテーブルの名前、ステータスの種類、またはsomething


1-わかりました。bit厳しいとは限りません。ほとんどは決してない。

7
Max Vernon