PCMagでこれらの記事を Itzik Ben-Gan で読みました。
シークして、パートIをスキャンする:オプティマイザが最適化しない場合
シークアンドユースキャンパートII:昇順のキー
現在、すべてのパーティション分割テーブルで「Grouped Max」の問題が発生しています。 max-IDを取得するために提供されている the trick Itzik Ben-Ganを使用していますが、実行されない場合もあります。
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
基本的な問題は、インデックスシークの後にTop演算子が続かないことです。これは、シークが_MIN\MAX
_集合体の正しい順序で行を返すときに通常導入される最適化です。
この最適化は、最小/最大行が昇順または降順の最初の行であるという事実を利用しています。また、オプティマイザがこの最適化をパーティションテーブルに適用できない場合もあります。忘れました。
とにかく、重要なのは、この変換を行わないと、実行プランは、パーティションごとに必要な1行ではなく、パーティションごとに_S.UpdatedID <= @IDColumnThresholdValue
_に該当するすべての行を処理することです。
質問でテーブル、インデックス、またはパーティション分割の定義を提供していないため、具体的に説明することはできません。インデックスがそのような変換をサポートすることを確認する必要があります。ほぼ同等に、MAX
をTOP (1) ... ORDER BY UpdatedID DESC
として表すこともできます。
これによりソートが行われる場合( TopNソート を含む)、インデックスが役に立たないことがわかります。例えば:
_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:パーティション化されたオブジェクトで、_MIN\MAX
_アグリゲートとトップの組み込みサポートを要求する アクティブなConnectアイテム があります。