web-dev-qa-db-ja.com

ルックアップテーブルのパーティション列値のパーティション削除はありませんか?

(以下に示すように)パーティション分割テーブルを作成し、4億8,000万行(アカウントあたり約181行)をシードしました。

インデックスを追加する前にベースラインクエリを実行しています。パーティション列で日付ルックアップを行っても、option(recompile)を追加した後でもパーティションが削除されないことに驚きました。それはパーティション化されたテーブルでどうですか?述語のパーティション列の値をハードコーディングするというよりは、実際の生活のようです。

最終的には、インデックスを追加して、質問がある場合はここに投稿します。この投稿で与えられた回答に納得できるまで、先に進みません。

    --step 2 (after creating db)
    ALTER DATABASE partitionresearch
    ADD FILEGROUP January
    GO
    ALTER DATABASE partitionresearch
    ADD FILEGROUP February
    GO
    ALTER DATABASE partitionresearch
    ADD FILEGROUP March
    GO
    ALTER DATABASE partitionresearch
    ADD FILEGROUP April
    GO
    ALTER DATABASE partitionresearch
    ADD FILEGROUP May
    GO
    ALTER DATABASE partitionresearch
    ADD FILEGROUP June
    GO
    ALTER DATABASE partitionresearch
    ADD FILEGROUP July
    GO
    ALTER DATABASE partitionresearch
    ADD FILEGROUP August
    GO
    ALTER DATABASE partitionresearch
    ADD FILEGROUP September
    GO
    ALTER DATABASE partitionresearch
    ADD FILEGROUP October
    GO
    ALTER DATABASE partitionresearch
    ADD FILEGROUP November
    GO
    ALTER DATABASE partitionresearch
    ADD FILEGROUP December
    GO
    --step 3
    -- Table Partitioning in SQL Server
        ALTER DATABASE [Partitionresearch]
        ADD FILE 
        (
        NAME = [PartJan],
        FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL14.mycompany2\MSSQL\DATA\PartJan.ndf',
            SIZE = 5080 KB, 
            MAXSIZE = UNLIMITED, 
            FILEGROWTH = 2040 KB
        ) TO FILEGROUP [January]

        ALTER DATABASE [Partitionresearch]
        ADD FILE 
        (
        NAME = [PartFeb],
        FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL14.mycompany2\MSSQL\DATA\PartFeb.ndf',
            SIZE = 5080 KB, 
            MAXSIZE = UNLIMITED, 
            FILEGROWTH = 2040 KB
        ) TO FILEGROUP [February]

        ALTER DATABASE [Partitionresearch]
        ADD FILE 
        (
        NAME = [PartMar],
        FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL14.mycompany2\MSSQL\DATA\PartMar.ndf',
            SIZE = 5080 KB, 
            MAXSIZE = UNLIMITED, 
            FILEGROWTH = 2040 KB
        ) TO FILEGROUP [March]

        ALTER DATABASE [Partitionresearch]
        ADD FILE 
        (
        NAME = [PartApr],
        FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL14.mycompany2\MSSQL\DATA\PartApr.ndf',
            SIZE = 5080 KB, 
            MAXSIZE = UNLIMITED, 
            FILEGROWTH = 2040 KB
        ) TO FILEGROUP [April]

        ALTER DATABASE [Partitionresearch]
        ADD FILE 
        (
        NAME = [PartMay],
        FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL14.mycompany2\MSSQL\DATA\PartMay.ndf',
            SIZE = 5080 KB, 
            MAXSIZE = UNLIMITED, 
            FILEGROWTH = 2040 KB
        ) TO FILEGROUP [May]

        ALTER DATABASE [Partitionresearch]
        ADD FILE 
        (
        NAME = [PartJun],
        FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL14.mycompany2\MSSQL\DATA\PartJun.ndf',
            SIZE = 5080 KB, 
            MAXSIZE = UNLIMITED, 
            FILEGROWTH = 2040 KB
        ) TO FILEGROUP [June]

        ALTER DATABASE [Partitionresearch]
        ADD FILE 
        (
        NAME = [PartJul],
        FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL14.mycompany2\MSSQL\DATA\PartJul.ndf',
            SIZE = 5080 KB, 
            MAXSIZE = UNLIMITED, 
            FILEGROWTH = 2040 KB
        ) TO FILEGROUP [July]

        ALTER DATABASE [Partitionresearch]
        ADD FILE 
        (
        NAME = [PartAug],
        FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL14.mycompany2\MSSQL\DATA\PartAug.ndf',
            SIZE = 5080 KB, 
            MAXSIZE = UNLIMITED, 
            FILEGROWTH = 2040 KB
        ) TO FILEGROUP [August]

        ALTER DATABASE [Partitionresearch]
        ADD FILE 
        (
        NAME = [PartSep],
        FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL14.mycompany2\MSSQL\DATA\PartSep.ndf',
            SIZE = 5080 KB, 
            MAXSIZE = UNLIMITED, 
            FILEGROWTH = 2040 KB
        ) TO FILEGROUP [September]

        ALTER DATABASE [Partitionresearch]
        ADD FILE 
        (
        NAME = [PartOct],
        FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL14.mycompany2\MSSQL\DATA\PartOct.ndf',
            SIZE = 5080 KB, 
            MAXSIZE = UNLIMITED, 
            FILEGROWTH = 2040 KB
        ) TO FILEGROUP [October]

        ALTER DATABASE [Partitionresearch]
        ADD FILE 
        (
        NAME = [PartNov],
        FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL14.mycompany2\MSSQL\DATA\PartNov.ndf',
            SIZE = 5080 KB, 
            MAXSIZE = UNLIMITED, 
            FILEGROWTH = 2040 KB
        ) TO FILEGROUP [November]

        ALTER DATABASE [Partitionresearch]
        ADD FILE 
        (
        NAME = [PartDec],
        FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL14.mycompany2\MSSQL\DATA\PartDec.ndf',
            SIZE = 5080 KB, 
            MAXSIZE = UNLIMITED, 
            FILEGROWTH = 2040 KB
        ) TO FILEGROUP [December]

    --step 4
    -- Table Partitioning in SQL Server
    USE Partitionresearch
    GO

    CREATE PARTITION FUNCTION [MonthlyPartition] (date)
    AS RANGE RIGHT FOR VALUES ('20190201', '20190301', '20190401',
                   '20190501', '20190601', '20190701', '20190801', 
                   '20190901', '20191001', '20191101', '20191201');

    --step 5
    -- Table Partitioning in SQL Server
    USE Partitionresearch
    GO

    CREATE PARTITION SCHEME MonthWisePartition
    AS PARTITION MonthlyPartition
            TO (January, February, March, April, May, June, July, 
                August, September, October, November, December
                );
    --step 6
    create table dbo.partitionresearch 
    (
    tranid int identity(1,1),
    [Date] date,

    Account int,
    SeqNumber tinyint,
    AlertType int,
    IsFirst tinyint,
    Indicator1 int,
    [time] time
    )
    on monthwisepartition([date])

    --with partitioning help - 40 seconds (as opposed to 3 min 46 sec) , hovered over table scan and didnt see partition count, but clearly partitions (elimination) were used
    --did see scalar operators with values 5 and 10 which happens to be where these accounts are partition wise (may and october)
    use partitionresearch
    select * from dbo.partitionresearch --hoverd over and closest thing to partn help i saw were scalar operators 5 and 10
    where (date between '5/1/2019' and '5/31/2019' or date between '10/1/2019' and '10/31/2019') and
          account in (1000000,2000000) 
    ------------------------------------------------------------------------------------------------------------------------
    --with "partition help" from a lookup table--3 minutes 33 seconds
    use partitionresearch
    select a.* from dbo.partitionresearch a--hovered over and believe partns wont be used
    join [dbo].[monthlookup] b
    on a.date=b.date
    where account in (1000000,2000000) 
    ------------------------------------------------------------------------------------------------------------------------
--this is the date lookup table which isnt partitioned, thus not aligned
USE [partitionresearch]
GO

/****** Object:  Table [dbo].[monthlookup]    Script Date: 7/12/2019 6:24:35 PM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[monthlookup](
    [monthid] [int] IDENTITY(1,1) NOT NULL,
    [Date] [date] NOT NULL
) ON [PRIMARY]
GO
7
user8591443

これは、行ストアパーティションヒープの製品では使用できません。テーブルを変更して、パーティション化されたクラスター化列ストアインデックスを持つようにすると、ビットマップフィルターによる行グループの削除によってパーティションを削除できる場合があります。

これについてブログに書いた here 。小さなセクションを引用する:

ディメンションテーブルのデータに基づいて、SQL Serverはファクトテーブルから2つのパーティションを読み取るだけでよいことがわかっています。クエリオプティマイザーは理論的にはそれよりも優れているでしょうか。パーティションテーブルには最大で15000のパーティションがあるという事実を考慮してください。すべてのパーティション値はオーバーラップできず、DDL操作がなければ変更されません。ハッシュテーブルを作成するとき、クエリオプティマイザーは、どのパーティションに少なくとも1つの行があるかを追跡できます。ハッシュビルドの終わりまでに、どのパーティションにデータを含めることができるかが正確にわかるため、残りのパーティションはプローブフェーズ中にスキップできます。

ハッシュのビルドがプローブから独立していることが重要であるため、おそらくこれは実装されていません。おそらく、再分割ストリームオペレーターとは対照的に、ビットマップオペレーターがスキャンまで押し下げられることが適切なタイミングで利用できる保証はありません。おそらくこれは一般的なケースではなく、最適化を行う価値はありません。結局のところ、パーティションカラムでフィルタリングする代わりに、どのくらいの頻度でパーティションカラムに参加しますか?

7
Joe Obbish

完全を期すために、動的パーティション除去を取得できますが、結合タイプが相関パラメーターを持つネストされたループである場合のみです。

たとえば、提供されているパーティション関数とスキームを使用します。

_CREATE PARTITION FUNCTION MonthlyPartition ([date])
AS RANGE RIGHT FOR VALUES
(
    '20190201', '20190301', '20190401',
    '20190501', '20190601', '20190701', '20190801', 
    '20190901', '20191001', '20191101', '20191201'
);

CREATE PARTITION SCHEME MonthWisePartition
AS PARTITION MonthlyPartition ALL TO ([PRIMARY]);
_

とテーブル:

_CREATE TABLE dbo.PartitionResearch 
(
    tranid integer identity(1,1) NOT NULL,
    [Date] date NULL,
    Account integer NULL,
    SeqNumber tinyint NULL,
    AlertType integer NULL,
    IsFirst tinyint NULL,
    Indicator1 integer NULL,
    [time] time NULL
)
ON MonthWisePartition([Date]);

CREATE TABLE dbo.MonthLookup
(
    [MonthId] integer IDENTITY(1,1) NOT NULL,
    [Date] date NOT NULL
)
ON [PRIMARY];
_

パーティション除去を使用しないクエリ:

_SELECT
    a.* 
FROM dbo.PartitionResearch AS a
JOIN dbo.MonthLookup AS b
    ON a.[Date]=b.[Date]
WHERE
    a.Account IN (1000000,2000000);
_

...次の計画を作成します:

nested loops plan

PartitionResearchテーブルスキャンのプロパティは、ループの各反復でMonthLookupテーブルの現在の日付を使用して動的パーティションの削除を示します。

Dynamic partition elimination

テーブルが空であるため、ここではそのオプションをお勧めします。あなたのケースでは、オプティマイザは推定コストの理由からハッシュ結合計画を優先しました。 (計画に示されているように)56行のMonthLookupテーブルがある場合、ネストされたループの代替では、単一のパーティションを56回スキャンします。オプティマイザーは(おそらく正しく)12個のパーティションすべてを1回スキャンする方が適切だと評価します。

動的パーティション削除を使用してデータをテストする場合は、OPTION (LOOP JOIN)クエリヒントを使用してそのような計画を取得できます。サンプルクエリによってアクセスされるパーティションが2つだけの場合、2つのパーティションが妥当な時間内にそれぞれ28回スキャンされる可能性があります。

より堅牢な一般的な戦略については、たとえば、_$PARTITION_関数、一時テーブル、または動的SQLを使用するなど、特定のT-SQLを記述してパーティションを削除する必要があります。

2
Paul White 9

このコミュニティによって与えられた方向性をよりよく理解した後、ルックアップテーブルに含まれる内容(つまり、結合なし)に基づいてSQLが動的に生成されるプロトタイプを試してみたくなりました。おそらく、パーティション列を年と月だけで構成される新しい列に変更した後。

再コンパイルオプションを使用して、必要に応じて1〜12個の変数に基づいて動的SQLを作成する必要がありました。代わりに(以下のコードを参照)、時間を節約するために、理論的には動的SQLを利用して、ルックアップテーブルとパーティションの削除に関連する(合理的な範囲内の)制限を回避できることを証明しました。

2本の弦を作りました。 2つの日付範囲変数宣言(@low1,@high1,@low2,@high2)。 1つは、日付範囲変数と2つのターゲットアカウントを参照する選択と述部を備えています。 2つの文字列の連結を実行しましたが、パーティションの削除が確実に使用されました。選択には最後にオプション(再コンパイル)がありました。あるトライアルから次のトライアルまでの実行時間を比較する前に、キャッシュがクリアされました。パーティションの削除が実際に発生したことを確認するためにも一部。

declare @sql1 varchar(500)=
'declare @low1 date ='+'''' +'5/1/19'+'''' +
'declare @low2 date ='+'''' +'10/1/19'+'''' +
'declare @high1 date ='+''''+'5/31/19' +'''' +
'declare @high2 date ='+''''+'10/31/19'+'''' 
declare @sql2 varchar(500)=
'select * from dbo.partitionresearch 
where (date between @low1 and @high1 or date between @low2 and @high2) and
      account in (1000000,2000000) 
OPTION (RECOMPILE)'
CHECKPOINT 
DBCC DROPCLEANBUFFERS
exec (@sql1+@sql2)
1
user8591443