SQL Serverカーディナリティヒント


14

カーディナリティの推定値をSQL Serverオプティマイザー(任意のバージョン)に「注入」する方法はありますか?

すなわち、Oracleのカーディナリティのヒントに似たものです。

私のモチベーションは、クエリオプティマイザーは本当にどれくらい良いのかという記事に基づいています。[1]、彼らは悪い計画の選択に対するカーディナリティ推定量の影響をテストします。したがって、複雑なクエリに対して正確にカーディナリティを「推定」するようにSQL Serverに強制できれば十分です。


[1] Leis、Viktor、et al。「クエリオプティマイザーはどれほど優れていますか?」
VLDB Endowment 9.3(2015):204-215の議事録。

回答:


10

Adam Machanicによって開発されたと呼ばれるユーザー定義関数をCARDINALITY戦略的に使用することTOPで、Oracleのヒントに似たものを得ることができます。いくつかの例を見ていきましょう。自由に利用できるAdventureWorksデータベースを使用しています。次のクエリで、派生テーブルから返される行の数を実際に制御する必要があるとします。MANY() th

SELECT 
    p.Name
    , th.ProductId
    , th.Quantity
    , th.ActualCost
FROM Production.Product p
INNER JOIN (
    SELECT ProductId, Quantity, ActualCost
    FROM Production.TransactionHistory 
) th ON p.ProductID = th.ProductID;

現状では、113443行の推定値が得られます。

開始クエリ

推定値を低くする必要がある場合は、クエリヒントと共にth使用しTOPOPTIMIZE FOR、行の目標を設定できます。これを行う1つの方法を次に示します。

DECLARE @row_goal BIGINT = 9223372036854775807;
SELECT 
    p.Name
    , th.ProductId
    , th.Quantity
    , th.ActualCost
FROM Production.Product p
INNER JOIN (
    SELECT TOP (@row_goal) ProductId, Quantity, ActualCost
    FROM Production.TransactionHistory 
) th ON p.ProductID = th.ProductID
OPTION (OPTIMIZE FOR (@row_goal = 1));

推定値が1行だけであることがわかります。

1行の見積もり

結果が変わらない@row_goalように、できるだけ大きなBIGINT値に設定します。OPTIMIZE FORクエリヒントがあるかのようにクエリをオプティマイザオプティマイザに指示します@row_goal、ISが1に等しい私は同じ結果を得ることができますが、クエリが異なって最適化されます。

カーディナリティの推定値を増やすことは、より複雑です。TOPオプティマイザは十分な行を返さないことを認識するため、値を増やすことはできません。ただし、MANY()関数を使用して推定値に行を追加できます。MANY()関数は常に0行を返しますが、それからの行推定値は入力パラメーターによって変化することに注意してください。派生テーブルからの行の推定値を10倍増やす必要があるとします。それを達成する1つの方法:

SELECT 
    p.Name
    , th.ProductId
    , th.Quantity
    , th.ActualCost
FROM Production.Product p
INNER JOIN (
    SELECT TOP (9223372036854775807) ProductId, Quantity, ActualCost
    FROM Production.TransactionHistory 
    LEFT OUTER JOIN dbo.Many(10) AS m ON 1=1
) th ON p.ProductID = th.ProductID;

推定値はベーステーブルの10倍であることがわかります。

10Xクエリ

TOPオプティマイザがテーブルを移動するのを防ぐために余分なものが追加されました。それなしでMANY()計画内の間違った場所に機能を適用できます。

行数に係数を掛けるだけでなく、正確な過大評価が必要な場合は、2つの手法を組み合わせることができます。たとえば、派生テーブルの推定値が正確に1000000行である必要があると仮定します。それを達成する1つの方法:

DECLARE @row_goal BIGINT = 9223372036854775807;

SELECT 
    p.Name
    , th.ProductId
    , th.Quantity
    , th.ActualCost
FROM Production.Product p
INNER JOIN (
    SELECT TOP (@row_goal) ProductId, Quantity, ActualCost
    FROM Production.TransactionHistory 
    LEFT OUTER JOIN dbo.Many(10) AS m ON
        1=1
) th ON p.ProductID = th.ProductID
OPTION (OPTIMIZE FOR (@row_goal = 1000000));

推定値は1000000行であることがわかります。

1 M行

これらは高度なテクニックであり、クエリの最適化には必要ないことが多いことに注意する必要があります。詳細については、 Adam Machanicが発表したClash of the Row Goalsをご覧ください。


dbo.Many関数

-- By Adam Machanic, reproduced with permission
IF EXISTS (SELECT * FROM sys.objects WHERE name = 'Many' AND OBJECT_SCHEMA_NAME(object_id) = 'dbo')
    DROP FUNCTION dbo.Many
GO
CREATE FUNCTION dbo.Many(@n INT)
RETURNS TABLE AS
RETURN
(
    WITH
    a(x) AS
    (
        SELECT
            *
        FROM
        (
            VALUES
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1)
        ) AS x0(x)
    )
    SELECT TOP(@n)
        1 AS x
    FROM
        a AS a1,
        a AS a2
    WHERE
        a1.x % 2 = 0
)
GO

9

カーディナリティ推定値をオプティマイザーに直接注入する方法はありませんが、達成したいものに応じていくつかのオプションがあります。

OPTION (FAST N)クエリヒントを使用して行の目標を導入し、CTEまたはサブクエリを使用してクエリを書き換えてTOP...ORDER BY、実行計画のさまざまな部分にベースの行の目標を挿入できますが、開始時に結果のクエリがどれほど効率的かはわかりませんより複雑な構造で遊んでいます。

より詳細な説明については、オプティマイザーの内部:行の目標の詳細を参照してください。

あなたは、オペレータに影響を与えるようにしたい場合は、必要ありませんオプティマイザピックは、カーディナリティの推定を注入しようとするが、あなたは次のようなものを使用することができることOPTION (MERGE JOIN)OPTION (HASH JOIN)、物理的、強制的に、たとえば、オペレータに参加。

この記事では、ヒントを使用してプランに影響を与える方法について詳しく説明します。ヒントによる実行プランの制御

計画を修正する場合は、計画ガイドを使用することもできます。

繰り返しますが、実際のユースケースが何であるかは明確ではありません。これらの手法を使用すると、走行距離が異なる場合があります。多くの場合、オプティマイザーが判断できるようにし、オプティマイザーが情報に基づいた決定を下せるように、最新の統計情報があることを確認することをお勧めします。


関連するMicrosoft Connectの提案:xor88によるクエリでフィルター選択性のヒントを指定できるようにします。マイクロソフトは応答しました:

フィードバックをお寄せいただきありがとうございます。これの潜在的な利点を見ることができます。一般に、自動動作を可能な限り良好にし、この種のヒントの必要性を避けるように努力しますが、もちろん他にも多くのヒントがあります。将来のリリースではこれを検討しますが、デナリ(11.0)バージョンを超える可能性があります。

よろしく、
エリックハンソン
プログラムマネージャー
SQL Serverクエリ処理


3

SQL Server OPTIMIZE FORクエリヒントを使用すると、コンパイル時に実際の値(パラメーター)または不明な値(変数)を使用する代わりに、ヒント値に基づいてカーディナリティの推定を強制できます。詳細については、SQL Serverのドキュメントのクエリヒントのトピックを参照してください。

たとえば、以下のクエリは、ローカル変数の場合のように全体的な平均カーディナリティの代わりに、ヒント値からの統計ヒストグラムに基づいて行数を推定します。

DECLARE 
      @StartDate datetime = '20150101'
    , @EndDate datetime = '20150102';
SELECT *
FROM dbo.Example
WHERE
    DateColumn BETWEEN  @StartDate AND @EndDate
OPTION(OPTIMIZE FOR(@StartDate = '20100101', @EndDate='20100101'));

同様に、コンパイル時に実際のパラメーター値の代わりにヒント値からの統計値のヒストグラムに基づいて推定が行われるように、パラメーターにヒントを使用できます。

DECLARE 
      @StartDate datetime = '20150101'
    , @EndDate datetime = '20150102';
EXECUTE sp_executesql N'SELECT *
        FROM dbo.Example
        WHERE
            DateColumn BETWEEN  @StartDate AND @EndDate
        OPTION(OPTIMIZE FOR(@StartDate = ''20100101'', @EndDate=''20100101''));'
    , N'@StartDate datetime, @EndDate datetime'
    , @StartDate = @StartDate
    , @EndDate = @EndDate;

UNKNOWNキーワードではなく、実際のパラメータ値と統計ヒストグラムに基づいて推定する全体的な平均カーディナリティを使用するヒントに代わりにリテラルを指定することができます。

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