シークし、パーティションテーブルでスキャンします…


22

Itzik Ben-Ganの PCMagでこれらの記事を読みました。

シークし、スキャンしますパートI:オプティマイザがシークを最適化しない場合、
スキャンしますパートII:昇順キー

現在、すべてのパーティションテーブルで「グループ化された最大」問題が発生しています。Itzik Ben-Ganが提供するトリックを使用し max(ID)を取得しますが、実行されない場合があります。

DECLARE @MaxIDPartitionTable BIGINT
SELECT  @MaxIDPartitionTable = ISNULL(MAX(IDPartitionedTable), 0)
FROM    ( SELECT    *
          FROM      ( SELECT    partition_number PartitionNumber
                      FROM      sys.partitions
                      WHERE     object_id = OBJECT_ID('fct.MyTable')
                                AND index_id = 1
                    ) T1
                    CROSS APPLY ( SELECT    ISNULL(MAX(UpdatedID), 0) AS IDPartitionedTable
                                  FROM      fct.MyTable s
                                  WHERE     $PARTITION.PF_MyTable(s.PCTimeStamp) = PartitionNumber
                                            AND UpdatedID <= @IDColumnThresholdValue
                                ) AS o
        ) AS T2;
SELECT @MaxIDPartitionTable 

私はこの計画を得る

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

しかし、45分後、読み取りを見てください

reads          writes   physical_reads
12,949,127        2       12,992,610

私が抜け出すことsp_whoisactive

通常、非常に高速に実行されますが、今日は実行されません。

編集:パーティションのあるテーブル構造:

CREATE PARTITION FUNCTION [MonthlySmallDateTime](SmallDateTime) AS RANGE RIGHT FOR VALUES (N'2000-01-01T00:00:00.000', N'2000-02-01T00:00:00.000' /* and many more */)
go
CREATE PARTITION SCHEME PS_FctContractualAvailability AS PARTITION [MonthlySmallDateTime] TO ([Standard], [Standard])
GO
CREATE TABLE fct.MyTable(
    MyTableID BIGINT IDENTITY(1,1),
    [DT1TurbineID] INT NOT NULL,
    [PCTimeStamp] SMALLDATETIME NOT NULL,
    Filler CHAR(100) NOT NULL DEFAULT 'N/A',
    UpdatedID BIGINT NULL,
    UpdatedDate DATETIME NULL
CONSTRAINT [PK_MyTable] PRIMARY KEY CLUSTERED 
(
    [DT1TurbineID] ASC,
    [PCTimeStamp] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, DATA_COMPRESSION = ROW) ON [PS_FctContractualAvailability]([PCTimeStamp])
) ON [PS_FctContractualAvailability]([PCTimeStamp])

GO

CREATE UNIQUE NONCLUSTERED INDEX [IX_UpdatedID_PCTimeStamp] ON [fct].MyTable
(
    [UpdatedID] ASC,
    [PCTimeStamp] ASC
)
INCLUDE (   [UpdatedDate]) 
WHERE ([UpdatedID] IS NOT NULL)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, DATA_COMPRESSION = ROW) ON [PS_FctContractualAvailability]([PCTimeStamp])
GO

回答:


28

基本的な問題は、インデックスシークの後にTop演算子が続かないことです。これは通常、シークがMIN\MAX集計の正しい順序で行を返すときに導入される最適化です。

この最適化は、最小/最大行が昇順または降順で最初の行であるという事実を利用します。また、オプティマイザーがこの最適化をパーティションテーブルに適用できないこともあります。忘れる

とにかく、ポイントは、この変換なしS.UpdatedID <= @IDColumnThresholdValueでは、パーティションごとに必要な1行ではなく、パーティションごとに修飾されるすべての行を処理することになります。

質問でテーブル、インデックス、またはパーティションの定義を提供していないため、これ以上具体的に説明することはできません。インデックスがこのような変換をサポートすることを確認する必要があります。ほぼ同等に、MAXとして表現することもできますTOP (1) ... ORDER BY UpdatedID DESC

これによりソートが行われた場合(TopN Sortを含む)、インデックスが役に立たないことがわかります。例えば:

SELECT
    @MaxIDPartitionTable = ISNULL(MAX(T2.IDPartitionedTable), 0)
FROM    
( 
    SELECT
        O.IDPartitionedTable
    FROM      
    ( 
        SELECT
            P.partition_number AS PartitionNumber
        FROM sys.partitions AS P
        WHERE 
            P.[object_id] = OBJECT_ID(N'fct.MyTable', N'U')
            AND P.index_id = 1
    ) AS T1
    CROSS APPLY 
    (    
        SELECT TOP (1) 
            S.UpdatedID AS IDPartitionedTable
        FROM fct.MyTable AS S
        WHERE
            $PARTITION.PF_MyTable(S.PCTimeStamp) = T1.PartitionNumber
            AND S.UpdatedID <= @IDColumnThresholdValue
        ORDER BY
            S.UpdatedID DESC
    ) AS O
) AS T2;

これにより生成される平面形状は次のとおりです。

望ましい平面形状

インデックスシークの下の上部に注目してください。これにより、処理がパーティションごとに1行に制限されます。

または、一時テーブルを使用してパーティション番号を保持します。

CREATE TABLE #Partitions
(
    partition_number integer PRIMARY KEY CLUSTERED
);

INSERT #Partitions
    (partition_number)
SELECT
    P.partition_number AS PartitionNumber
FROM sys.partitions AS P
WHERE 
    P.[object_id] = OBJECT_ID(N'fct.MyTable', N'U')
    AND P.index_id = 1;

SELECT
    @MaxIDPartitionTable = ISNULL(MAX(T2.UpdatedID), 0)
FROM #Partitions AS P
CROSS APPLY 
(
    SELECT TOP (1) 
        S.UpdatedID
    FROM fct.MyTable AS S
    WHERE
        $PARTITION.PF_MyTable(S.PCTimeStamp) = P.partition_number
        AND S.UpdatedID <= @IDColumnThresholdValue
    ORDER BY
        S.UpdatedID DESC
) AS T2;

DROP TABLE #Partitions;

サイドノート:クエリでシステムテーブルにアクセスすると、並列処理が妨げられます。これが重要な場合は、パーティション番号を一時テーブルにマテリアライズしてAPPLYから、それを使用することを検討してください。並列処理は通常、このパターンでは(正しいインデックス付けでは)役に立たないが、それは言うまでもありません。

サイドノート2:集約オブジェクトとパーティションオブジェクトのTopの組み込みサポートを要求するアクティブなConnectアイテムがありMIN\MAXます。

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