(以下に示すように)パーティション分割テーブルを作成し、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
これは、行ストアパーティションヒープの製品では使用できません。テーブルを変更して、パーティション化されたクラスター化列ストアインデックスを持つようにすると、ビットマップフィルターによる行グループの削除によってパーティションを削除できる場合があります。
これについてブログに書いた here 。小さなセクションを引用する:
ディメンションテーブルのデータに基づいて、SQL Serverはファクトテーブルから2つのパーティションを読み取るだけでよいことがわかっています。クエリオプティマイザーは理論的にはそれよりも優れているでしょうか。パーティションテーブルには最大で15000のパーティションがあるという事実を考慮してください。すべてのパーティション値はオーバーラップできず、DDL操作がなければ変更されません。ハッシュテーブルを作成するとき、クエリオプティマイザーは、どのパーティションに少なくとも1つの行があるかを追跡できます。ハッシュビルドの終わりまでに、どのパーティションにデータを含めることができるかが正確にわかるため、残りのパーティションはプローブフェーズ中にスキップできます。
ハッシュのビルドがプローブから独立していることが重要であるため、おそらくこれは実装されていません。おそらく、再分割ストリームオペレーターとは対照的に、ビットマップオペレーターがスキャンまで押し下げられることが適切なタイミングで利用できる保証はありません。おそらくこれは一般的なケースではなく、最適化を行う価値はありません。結局のところ、パーティションカラムでフィルタリングする代わりに、どのくらいの頻度でパーティションカラムに参加しますか?
完全を期すために、動的パーティション除去を取得できますが、結合タイプが相関パラメーターを持つネストされたループである場合のみです。
たとえば、提供されているパーティション関数とスキームを使用します。
_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);
_
...次の計画を作成します:
PartitionResearch
テーブルスキャンのプロパティは、ループの各反復でMonthLookup
テーブルの現在の日付を使用して動的パーティションの削除を示します。
テーブルが空であるため、ここではそのオプションをお勧めします。あなたのケースでは、オプティマイザは推定コストの理由からハッシュ結合計画を優先しました。 (計画に示されているように)56行のMonthLookup
テーブルがある場合、ネストされたループの代替では、単一のパーティションを56回スキャンします。オプティマイザーは(おそらく正しく)12個のパーティションすべてを1回スキャンする方が適切だと評価します。
動的パーティション削除を使用してデータをテストする場合は、OPTION (LOOP JOIN)
クエリヒントを使用してそのような計画を取得できます。サンプルクエリによってアクセスされるパーティションが2つだけの場合、2つのパーティションが妥当な時間内にそれぞれ28回スキャンされる可能性があります。
より堅牢な一般的な戦略については、たとえば、_$PARTITION
_関数、一時テーブル、または動的SQLを使用するなど、特定のT-SQLを記述してパーティションを削除する必要があります。
このコミュニティによって与えられた方向性をよりよく理解した後、ルックアップテーブルに含まれる内容(つまり、結合なし)に基づいて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)