How to Partition an existing SQL Server Table

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
GO

Looking 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%'
objectnameindexnamepartition_idpartition_numberrows
TABLE1PK_TABLE1720575940427120641999
TABLE1IX_TABLE1_col2col3720575940427776001999

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_id

Now 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)
GO

Once 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.

objectnameindexnamepartition_idpartition_numberrows
TABLE1IX_TABLE1_partitioncol720575940430397441233
TABLE1IX_TABLE1_partitioncol720575940431052802365
TABLE1IX_TABLE1_partitioncol720575940431708163366
TABLE1IX_TABLE1_partitioncol72057594043236352435
TABLE1IX_TABLE1_col2col3720575940433018881999
TABLE1PK_TABLE1720575940433674241999

Sample SQL Server Table and Data Cleanup

--cleanup
DROP TABLE TABLE1
DROP PARTITION SCHEME myPartitionScheme
DROP PARTITION FUNCTION myDateRangePF

Next Steps

5 Comments

  1. 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

  2. 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.

  3. 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.

  4. 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

  5. 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

Leave a Reply

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