Rebuilding One Partition Online Instead of the Whole Index

You can rebuild one partition online instead of the whole index, and on a big table that can save hours. The catch is finding the right partition. A partition number is a position in the partition function, not a month name, and positions move.

A fluting iron shaping one pleat while the other pleats remain untouched

Why not rebuild everything

Imagine a large table partitioned by month. Last month’s data was loaded in a hurry, and its index pages are scattered. The nightly job rebuilds the whole index, and it grinds through years of old data that nobody has touched. Only one partition needed attention.

ALTER INDEX can rebuild a single partition, and it can do it online. Let me show the mechanics on a small partitioned table. The demo creates a partition function, a scheme and a table, and the last block drops them.

Build a small partitioned table

I use RANGE RIGHT with two boundaries, January 1 and February 1. With RANGE RIGHT, a row dated exactly on a boundary belongs to the partition on its right. The last query shows which partition each row lands in.

DROP TABLE IF EXISTS dbo.PartitionRebuildDemo;
IF EXISTS (SELECT 1 FROM sys.partition_schemes WHERE name = N'psRebuildDemo') DROP PARTITION SCHEME psRebuildDemo;
IF EXISTS (SELECT 1 FROM sys.partition_functions WHERE name = N'pfRebuildDemo') DROP PARTITION FUNCTION pfRebuildDemo;
GO
CREATE PARTITION FUNCTION pfRebuildDemo (date) AS RANGE RIGHT FOR VALUES ('20260101', '20260201');
CREATE PARTITION SCHEME psRebuildDemo AS PARTITION pfRebuildDemo ALL TO ([PRIMARY]);

CREATE TABLE dbo.PartitionRebuildDemo (OrderDate date, Id int, Payload char(100));
CREATE CLUSTERED INDEX CX_PartitionRebuildDemo
    ON dbo.PartitionRebuildDemo (OrderDate, Id) ON psRebuildDemo (OrderDate);

INSERT dbo.PartitionRebuildDemo (OrderDate, Id, Payload)
VALUES ('20251231', 1, 'old'), ('20260101', 2, 'January'), ('20260201', 3, 'February');

SELECT OrderDate, Id, $PARTITION.pfRebuildDemo(OrderDate) AS PartitionNumber
FROM dbo.PartitionRebuildDemo ORDER BY OrderDate, Id;

December 31 is in partition 1. January 1 is in partition 2, and February 1 is in partition 3. Notice that the boundary date itself goes to the right-hand partition.

Rebuild one partition online

Now rebuild only partition 3, the February one. The ONLINE option keeps the table available for reads and writes while the rebuild runs. Afterwards I check the row count per partition and the statistics on the table.

ALTER INDEX CX_PartitionRebuildDemo ON dbo.PartitionRebuildDemo
REBUILD PARTITION = 3 WITH (ONLINE = ON);

SELECT partition_number, rows
FROM sys.partitions
WHERE object_id = OBJECT_ID(N'dbo.PartitionRebuildDemo') AND index_id = 1
ORDER BY partition_number;

SELECT name, is_incremental, STATS_DATE(object_id, stats_id) AS StatisticsUpdated
FROM sys.stats
WHERE object_id = OBJECT_ID(N'dbo.PartitionRebuildDemo')
ORDER BY name;
SQL Server results showing partition membership and rows after an online rebuild
Partition membership, rows per partition after the rebuild, and the statistics row for the index.

The screenshot shows the results of this block and the previous one. Each partition still holds one row, so the rebuild lost nothing. The statistics row shows is_incremental 0 and no update date in this tiny demo. So I would not count on a partition rebuild to refresh statistics. Keep that in your regular maintenance.

Look before you rebuild

Don’t rebuild by habit. Ask about that one partition first. The physical stats function takes a partition number, so you can inspect just the one you care about.

SELECT partition_number, index_level, page_count, avg_fragmentation_in_percent
FROM sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID(N'dbo.PartitionRebuildDemo'), 1, 3, 'DETAILED')
WHERE index_level = 0;

SELECT SERVERPROPERTY('Edition') AS Edition;

Partition 3 has 1 page and 0.0 percent fragmentation. A rebuild here would be pointless, and a high percentage on a partition this small would not matter either. Size and workload decide, not the percentage alone. The second query matters too: online index operations need an edition that supports them. My test server reports a Developer edition, so check yours before you rely on ONLINE.

Partition numbers move

Here is the trap. February is partition 3 today. Add a boundary for an earlier month and every number after it shifts. A job that says PARTITION = 3 will quietly rebuild the wrong month. So ask SQL Server for the number from the date, every time.

SELECT $PARTITION.pfRebuildDemo('20260201') AS FebruaryPartitionBefore;

ALTER PARTITION SCHEME psRebuildDemo NEXT USED [PRIMARY];
ALTER PARTITION FUNCTION pfRebuildDemo() SPLIT RANGE ('20251201');

SELECT $PARTITION.pfRebuildDemo('20260201') AS FebruaryPartitionAfter;

DECLARE @p int = $PARTITION.pfRebuildDemo('20260201');
ALTER INDEX CX_PartitionRebuildDemo ON dbo.PartitionRebuildDemo
REBUILD PARTITION = @p WITH (ONLINE = ON);

SELECT partition_number, rows
FROM sys.partitions
WHERE object_id = OBJECT_ID(N'dbo.PartitionRebuildDemo') AND index_id = 1
ORDER BY partition_number;

February moves from partition 3 to partition 4 after the split. The variable finds it, and the rebuild targets the right one. The last result shows four partitions now, with one row in each of partitions 2, 3 and 4.

On your own server, measure the log space, the locking and the query benefit on real data before you change a maintenance job. Then clean up.

DROP TABLE IF EXISTS dbo.PartitionRebuildDemo;
IF EXISTS (SELECT 1 FROM sys.partition_schemes WHERE name = N'psRebuildDemo') DROP PARTITION SCHEME psRebuildDemo;
IF EXISTS (SELECT 1 FROM sys.partition_functions WHERE name = N'pfRebuildDemo') DROP PARTITION FUNCTION pfRebuildDemo;
A safe partition rebuild

Find the partition from the date, then rebuild only that one.

A partition number is not a month name, it is a position that can move.

Published by Pinal Dave on SQLAuthority. More of my work at pinaldave.com.


Discover more from SQL Authority with Pinal Dave

Subscribe to get the latest posts sent to your email.

SQL Index, SQL Server, Table Partitioning
Previous Post
Enforcing Object Naming Rules With a DDL Trigger
Next Post
SQL SERVER – The Story of a Lesser Known Startup Parameter in SQL Server – Guest Post by Balmukund Lakhani

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.