web-dev-qa-db-ja.com

クエリを最適化して論理読み取りを削減する

127K行を返すSQL Server 2016クエリがあります。 クエリとクエリプランはこちら です。テーブル構造も必要な場合はお知らせください。

20行のみのテーブルと結合する必要があります。これは、いずれかの製品の代替として機能します。つまり、メインテーブルから製品をクエリしますが、特定の条件下では、一部の製品を別の製品で置き換えることができます。

問題は、その単純なテーブルの場合、254Kの論理読み取りがあることです。私はもう試した LEFT JOINおよびOUTER APPLY

この量の論理読み取りを回避するためにこれを置き換える方法について何か提案はありますか?言うまでもなく、代替品があるのは1つの製品だけです。

1
Mauricio

実行計画のXMLを詳しく見てみると、次の問題のある統計に注意してください。

<Wait WaitType="ASYNC_NETWORK_IO" WaitTimeMs="1328" WaitCount="403"/> 

<QueryTimeStats CpuTime="353" ElapsedTime="1853"/>

クエリは、アプリケーションが消費する結果を1.3秒待機しました。クエリは合計1.8秒しか実行されませんでした。したがって、ここでの主な問題は、アプリケーションがこれらの127kの結果を行ごとに消費していることです。クエリ自体はかなり高速に実行されます。

フォレストマクダニエルには、この問題を示す優れたブログ投稿があります。 2つのEasy ASYNC_NETWORK_IOデモ

残りの回答では、質問の「論理読み取り」の部分を扱います。


OUTER APPLY 'dテーブル(DBVAREKT)に対するこれらすべての論理読み取りの理由は次のとおりです。

screenshot of index seek in plan Explorer

そこにある「インデックスシーク」は、NESTED LOOPS結合への上部入力の各行に対して1回実行されます。したがって、最終的には一致する行が302行しか返されない場合でも、そのインデックス(ID1)へのシークは127,329回です。

オプティマイザは通常、インデックスへの多くのシークを実行することを選択しませんが、上部の入力に82行があると考えていました。 82シークを行う方が間違いなくより合理的です。


この問題を解決する一般的な方法は、その特定のテーブルでNESTED LOOPS結合を行わないことです。これが問題の原因であるためです。そのためには、結合ヒントを使用できますが、ヒントを適用する場所を判断するのは少し難しいです。

Randi は、データの一部を一時テーブルに配置するクエリの可能なリライトについて言及し、SQL Serverオプティマイザがより適切に処理できる小さなチャンクにクエリを本質的に分割します。これは、次のようにOUTER APPLYで分割できます。

SELECT 
       1756,
       L.MADTYPE,
       DBM.VNR1
INTO #results
FROM dbo.STDORDRE S
INNER JOIN dbo.STDORD STO ON sto.DATO = s.DATO
                           AND sto.KUNDE = s.KUNDE
LEFT JOIN dbo.STDORDML L ON L.ONR = s.ONR
CROSS APPLY (SELECT dbo.MCS_ClarionDateToSQL(D.DATO) SQL_DATO,
                    DATEPART(dw, dbo.MCS_ClarionDateToSQL(D.DATO)) DP,  D.VNR1, D.DATO, D.LINE, D.KATALOGNR, D.VFAKTOR, D.MADTYPE FROM dbo.DBMENU D WHERE D.SNR = s.VARENR AND D.LINE = L.MENULINE) DBM
INNER JOIN dbo.DBKUNDE kun ON kun.NR = s.KUNDE
INNER JOIN dbo.DBKUNGRP dbk ON dbk.NR = kun.GRP
INNER JOIN dbo.DBVARE varm ON varm.NR = s.VARENR
INNER JOIN dbo.DBVARE var ON var.NR = DBM.VNR1
LEFT OUTER JOIN dbo.MENORDRE MEN ON MEN.KUNDE = s.KUNDE
                                AND MEN.DATO  = DBM.DATO
                                AND MEN.LINIE = DBM.LINE
                                AND MEN.NR    = s.MNR
WHERE 1 = 1
  AND ( kun.UDSKREVET = 0
                     OR ( kun.UDSKREVET = 1
                          AND kun.UDSDATO >= 79627 ))
                   AND varm.TYPE = 9
                   AND varm.KPFIX = 0
                   AND s.ML = 1
                   AND DBM.DATO BETWEEN 79627 AND 79777
                   AND sto.TYPE = 1;

SELECT 
       1756,
       L.MADTYPE,
       DBM.VNR1
FROM #results RES  
OUTER APPLY (SELECT MTY.PREC MTY_PREC, 
                    MTY.NR   MTY_NR ,
                    VAR1.PREC VAR1_PREC, 
                    var1.VAR_PKG_ID VAR1_VAR_PKG_ID,
                    VAR1.NR VAR1_NR ,
                    VAR1.TYPE VAR1_TYPE,
                    VAR1.GRP VAR1_GRP
             FROM dbo.DBVAREKT VKT
              LEFT JOIN dbo.DBVARE VAR1 ON VAR1.PREC = VKT.TO_VARE_PREC
                                               AND (ISNULL(VKT.MADTYPE,0) <> 0  )
              LEFT JOIN dbo.DBMTYPE MTY ON MTY.PREC = VKT.TO_MADTYPE_PREC
            WHERE 1 = 1
              AND VKT.MADTYPE = RES.MADTYPE
              AND VKT.VARENR  = RES.VNR1
                      ) OA;
7
Josh Darnell