SQL Server 2016のSUBSTRING()を含む述語の見積もりの​​変更?


13

SUBSTRING()または他の文字列関数を含む述語のカーディナリティを推定する方法に関するSQL Server 2016の変更に関するドキュメントまたは調査はありますか?

私が尋ねている理由は、互換モード130でパフォーマンスが低下したクエリを見て、SUBSTRING()の呼び出しを含むWHERE句に一致する行数の推定値の変更に関係した理由です。クエリの書き換えで問題を修正しましたが、SQL Server 2016のこの領域の変更に関するドキュメントを誰かが知っているかどうか疑問に思っています。

デモコードは次のとおりです。このテストケースでは推定値は非常に近いものですが、精度はデータによって異なります。

テストケースでは、compatレベル120ではSQL Serverは推定にヒストグラムを使用しているように見えますが、compatレベル130ではSQL Serverはテーブルの10%が固定されていると仮定しています。

CREATE DATABASE MyStringTestDB;
GO
USE MyStringTestDB;
GO
DROP TABLE IF EXISTS dbo.StringTest;
CREATE TABLE dbo.StringTest ( [TheString] varchar(15) );
GO
INSERT INTO dbo.StringTest
VALUES
( 'Y5_CLV' );
INSERT INTO dbo.StringTest
VALUES
( 'Y5_EG3' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_NE' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_PQT' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_T2V' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_TT4' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_ZKK' );
INSERT INTO dbo.StringTest
VALUES
( 'ZZ_LW6' );
INSERT INTO dbo.StringTest
VALUES
( 'ZZ_QO3' );
INSERT INTO dbo.StringTest
VALUES
( 'ZZ_TZ7' );
INSERT INTO dbo.StringTest
VALUES
( 'ZZ_UZZ' );

CREATE CLUSTERED INDEX IX_Clustered ON dbo.StringTest (TheString);

/* 
Uses fixed % for estimate; 1.1 rows estimated in this case.
    Plan for computation:
        CSelCalcFixedFilter (0.1) <----
            Selectivity: 0.1
*/
ALTER DATABASE MyStringTestDB SET compatibility_level = 130;
GO
SELECT * 
FROM dbo.StringTest 
WHERE SUBSTRING(TheString, 1, CHARINDEX('_',TheString) - 1) = 'ZZ'
OPTION (QUERYTRACEON 2363, QUERYTRACEON 3604);

/* 
Uses histogram to get estimate of 1
 CSelCalcPointPredsFreqBased <----
      Distinct value calculation:
          CDVCPlanLeaf
              0 Multi-Column Stats, 1 Single-Column Stats, 0 Guesses
      Individual selectivity calculations:
          (none)
    Loaded histogram for column QCOL: [DBA].[dbo].[StringTest].TheString from stats with id 1
*/
ALTER DATABASE MyStringTestDB SET compatibility_level = 120;
GO
SELECT * 
FROM dbo.StringTest 
WHERE SUBSTRING(TheString, 1, CHARINDEX('_',TheString) - 1) = 'ZZ'
OPTION (QUERYTRACEON 2363, QUERYTRACEON 3604);

/*
-- Simpler rewrite; works fine in both compat levels and gets better estimate.
SELECT * 
FROM dbo.StringTest 
WHERE TheString LIKE 'ZZ[_]%'
OPTION (QUERYTRACEON 2363, QUERYTRACEON 3604);
*/

1
特定の質問については定かではありませんが、Y5_EG3文字列が単なるコードで常に大文字の場合は、バイナリ照合順序を常に指定してみてくださいLatin1_General_100_BIN2--これにより、フィルタリング操作の速度が向上します。ステートメントの直後に追加COLLATE Latin1_General_100_BIN2します。それが計画の生成/推定にも影響を与えるかどうかを確認したいと思います。CREATE TABLEvarchar(15)
ソロモンラッツキー

回答:


8

私はドキュメントを知りません。私はこれを調査しましたが、コメントするには長すぎます。

10%の見積もりは、必ずしも低下ではありません。次の例をご覧ください。

TRUNCATE TABLE dbo.StringTest

INSERT INTO dbo.StringTest
SELECT TOP (1000000) 'ZZ_' + LEFT(NEWID(), 12)
FROM   master..spt_values v1,
       master..spt_values v2;

そしてWHEREあなたの質問の条項。

WHERE SUBSTRING(TheString, 1, CHARINDEX('_',TheString) - 1) = 'ZZ'

テーブルには100万行が含まれています。それらはすべて述語と一致します。互換性レベル130では、10%の推測では100,000の推定値が得られます。120未満の推定行は1.03913です。

120ビヘイビアーはヒストグラムを使用しますが、個別の行の数を取得するためだけです。私の場合の密度ベクトルは1.039131E-06を示しており、これにテーブルのカーディナリティが乗算されて、推定行カウントが取得されます。すべての値は実際には異なりますが、すべて述語と一致します。

query_optimizer_estimate_cardinality拡張イベントをトレースすると、130未満には2つの異なる<StatsCollection Name="CStCollFilter"イベントがあることがわかります。最初のものは100,000と推定されます。2番目の関数はヒストグラムを読み込み、CSelCalcPointPredsFreqBased / DistinctCountCalculatorを使用して1.04の推定値を取得します。この2番目の結果は未使用のようです。

130であなたが観察した動作は一貫して適用されませんORDER BY TheString。120行が1行のメモリ許可に苦労するので、130行の見積もりでこれが明らかな勝利になると期待しましたが、 130の場合も1.03913です。

追加OPTION (QUERYRULEOFF SelectToFilter)すると、ソートに入る推定値は100,000に戻りますが、メモリ許可は増加せず、ソートから出る推定値は依然としてテーブルの個別の値に基づいています。

ここに画像の説明を入力してください

同様に、130の場合、クエリが並列プランを取得するように並列処理のコストしきい値を調整するだけで、より低い推定値に戻すことができました。追加するQUERYTRACEON 8757と、推定値が低くなります。10%の見積もりは、些細な計画でのみ保持されるようです。

あなたの提案された書き換え

WHERE TheString LIKE 'ZZ[_]%'

両方よりもはるかに優れた推定値を示しています。これの出力は

  CSelCalcTrieBased

      Column: QCOL: [MyStringTestDB].[dbo].[StringTest].TheString

それが試行を使用したことを示す。これに関する詳細は、上記の文字列要約統計セクションにあります

ただし、元のクエリとは異なります。の最初のインスタンスは、_動的に検出されるのではなく、常に3番目の文字であると想定されています。

この仮定が元のクエリにハードコーディングされている場合

 WHERE SUBSTRING(TheString, 1, 3) = 'ZZ_'

推定方法がに変わりCSelCalcHistogramComparison(INTERVAL)、推定行が正確になります。

それを範囲に変換することができます

WHERE TheString >=  'ZZ_' AND TheString < ???

ヒストグラムを使用して、その範囲の値を持つ行の数を推定します。

ただし、これはカーディナリティの推定にのみ適用されます。LIKE実行時に範囲検索を使用できるため、望ましいです。SUBSTRING(TheString, 1, 3)またはLEFT(TheString, 3)できない。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.