Problem
I have read many articles on SQL Server partitioning and how to create a partitioned table. However, I have an existing SQL Server database that has a few very large tables that could benefit from partitioning. What are the steps required to partition an already existing table? Check out this tip to learn more.
Solution
There are two different approaches we could use to accomplish partitioning an existing SQL Server table. The first would be to create a brand-new partitioned table (you can do this by following this tip). Then simply copy the data from your existing table into the new table and do a table rename. Alternatively, as I will outline below, we can partition the table in place simply by rebuilding or creating a clustered index on the table.
Sample SQL Server Table and Data
--Table/Index creation
CREATE TABLE [dbo].[TABLE1]
([pkcol] [int] NOT NULL,
[datacol1] [int] NULL,
[datacol2] [int] NULL,
[datacol3] [varchar](50) NULL,
[partitioncol] datetime)
GO
ALTER TABLE dbo.TABLE1 ADD CONSTRAINT PK_TABLE1 PRIMARY KEY CLUSTERED (pkcol)
GO
CREATE NONCLUSTERED INDEX IX_TABLE1_col2col3 ON dbo.TABLE1 (datacol1,datacol2)
WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON [PRIMARY]
GO
-- Populate table data
DECLARE @val INT
SELECT @val=1
WHILE @val < 1000
BEGIN
INSERT INTO dbo.Table1(pkcol, datacol1, datacol2, datacol3, partitioncol)
VALUES (@val,@val,@val,'TEST',getdate()-@val)
SELECT @val=@val+1
END
GOLooking at the sys.partitions system view we can see we have created a regular single partition table.
SELECT o.name objectname,i.name indexname, partition_id, partition_number, [rows]
FROM sys.partitions p
INNER JOIN sys.objects o ON o.object_id=p.object_id
INNER JOIN sys.indexes i ON i.object_id=p.object_id and p.index_id=i.index_id
WHERE o.name LIKE '%TABLE1%'| objectname | indexname | partition_id | partition_number | rows |
|---|---|---|---|---|
| TABLE1 | PK_TABLE1 | 72057594042712064 | 1 | 999 |
| TABLE1 | IX_TABLE1_col2col3 | 72057594042777600 | 1 | 999 |
SQL Server Partitioned Table Creation
In order to create a partitioned table we’ll need to first create a partition function and partition scheme. For our example we are going to partition the table based on the datetime column. Here is the code to create these objects and check some of their metadata in the system views.
CREATE PARTITION FUNCTION myDateRangePF (datetime)
AS RANGE RIGHT FOR VALUES ('20110101', '20120101','20130101')
GO
CREATE PARTITION SCHEME myPartitionScheme
AS PARTITION myDateRangePF ALL TO ([PRIMARY])
GO
SELECT ps.name,pf.name,boundary_id,value
FROM sys.partition_schemes ps
INNER JOIN sys.partition_functions pf ON pf.function_id=ps.function_id
INNER JOIN sys.partition_range_values prf ON pf.function_id=prf.function_idNow that we have a partition scheme we can go ahead and partition our table. The plan is to partition the table using a clustered index. However, our table already has a clustered index defined. In this situation, we’ll need to drop this index first and recreate the constraint using a non-clustered index. If our table did not have a clustered index, we could omit this step. We can just run the CREATE CLUSTERED INDEX statement. Similarly, if our table had a clustered index defined on same column that we plan to partition the table on we could run the CREATE CLUSTERED INDEX statement with the DROP_EXISTING clause. Finally, if you are concerned about the downtime and have Enterprise Edition, you could use the ONLINE=ON option of the CREATE INDEX statement. This will minimize any downtime for your application. Keep in mind that you may see some performance degradation while the index is being rebuilt using the ONLINE option. Here is the script that we can use in our scenario.
ALTER TABLE dbo.TABLE1 DROP CONSTRAINT PK_TABLE1
GO
ALTER TABLE dbo.TABLE1 ADD CONSTRAINT PK_TABLE1 PRIMARY KEY NONCLUSTERED (pkcol)
WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
CREATE CLUSTERED INDEX IX_TABLE1_partitioncol ON dbo.TABLE1 (partitioncol)
WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON myPartitionScheme(partitioncol)
GOOnce this statements completes we can again take a look at the sys.partitions system view (see code above). The code confirm our table now has 4 partitions.
| objectname | indexname | partition_id | partition_number | rows |
|---|---|---|---|---|
| TABLE1 | IX_TABLE1_partitioncol | 72057594043039744 | 1 | 233 |
| TABLE1 | IX_TABLE1_partitioncol | 72057594043105280 | 2 | 365 |
| TABLE1 | IX_TABLE1_partitioncol | 72057594043170816 | 3 | 366 |
| TABLE1 | IX_TABLE1_partitioncol | 72057594043236352 | 4 | 35 |
| TABLE1 | IX_TABLE1_col2col3 | 72057594043301888 | 1 | 999 |
| TABLE1 | PK_TABLE1 | 72057594043367424 | 1 | 999 |
Sample SQL Server Table and Data Cleanup
--cleanup
DROP TABLE TABLE1
DROP PARTITION SCHEME myPartitionScheme
DROP PARTITION FUNCTION myDateRangePFNext Steps
- Read more tips on partitioning
- Use partitions to archive data from SQL Server
- Read more on other types of partitioning
Ben Snaidero has been a Database Administrator for just over 10 years. Starting out working mainly with Oracle he got into SQL Server in 2005 and has worked primarily with SQL Server for the last 3 years. His main focus with both Oracle and SQL Server is in the area of performance tuning.
- MSSQLTips Awards: Achiever (75+ tips) – 2018 | Author of the Year Contender – 2016-2017

Mitch Said right. Indexes should be aligned. Just the author shared half knowledge which does not help in real time.
1) Add partition key column also to the existing primary key. Also keep/create a function for validation of its PK
2) No FK will added here. Instead keep a function and validate the value is in primary key or not
I am little confused here. As mentioned by one of the user dropping the constraint will drop the existing. We can recreate the clustered index on the partition but what about the primary key? Do we need to create a non-clustered index with the same columns as earlier in the dropped clustered index? how other table FKs referencing earlier to the primary key will work as per your scenario.
The article ought to mention that when you recreate the clustered index, you will move the data in the table. Removing the clustered index effectively turns the table into a heap. When you recreate the clustered index sql does not remember that the table used to have a Clustered Index so doesn’t know that the data is actually in order. That could be significant if there are millions or billions of rows in the table. It may even be impossible if the file group holding the primary partition is nearing capacity.
Thank you so much for great explanation.. would you please do this partition by range. I am really want to know how we can do it for partition by range. Thanks in advance
Great article but is missing some pretty important details. The article’s intent was to show how to and FK to a partitioned table. Which it clearly does, kind-of. What is does not mention is that partition switching will not work. The table and indexes are not aligned. The table is partitioned but the primary key is not. The only way to get the primary key aligned is to associate it with the partition scheme and to do that the partitioning column has to be added to the index. Then trying to add a FK fails. Full 360 degree circle and back to the start. HTH someone. Mitch