Partitioning a table can greatly enhance performance and manageability, particularly with large datasets. In this article, we will walk you through the process of creating a partitioned table in SQL Server using the AdventureWorks sample database. This practical example will illustrate how to set up a partitioned table based on order dates.

1. Introduction to Table Partitioning

Partitioning involves dividing a table into smaller, more manageable pieces, yet still presenting it as a single table to users. This is particularly useful for tables with a large volume of data, as it can improve query performance and make data management more efficient.

2. Creating the Partition Function

The partition function determines how data is distributed across partitions. In our example, we will partition data based on DATETIME values, creating ranges for different years.

CREATE PARTITION FUNCTION pf_orders_date_range (DATETIME)
AS RANGE LEFT FOR VALUES ('2011-01-01', '2012-01-01', '2013-01-01');
  • pf_orders_date_range is the name of the partition function.
  • RANGE LEFT indicates that the range values specified are inclusive on the left and exclusive on the right.
  • The function will create partitions for dates up to but not including January 1 of the subsequent years.

3. Adding Filegroups and Files

Filegroups are used to organize data files and optimize storage. We will create three filegroups, each corresponding to a year, and then add data files to these filegroups.

Adding Filegroups

-- Add Filegroup for 2011
ALTER DATABASE advworks
ADD FILEGROUP fg_orders_201101;

-- Add Filegroup for 2012
ALTER DATABASE advworks
ADD FILEGROUP fg_orders_201201;

-- Add Filegroup for 2013
ALTER DATABASE advworks
ADD FILEGROUP fg_orders_201301;

Adding Files

-- Add File for 2011
ALTER DATABASE advworks
ADD FILE 
(
    NAME = 'Partition1_File',
    FILENAME = 'C:\tmp\dummy\fg_orders_201101.ndf',
    SIZE = 100MB,
    MAXSIZE = UNLIMITED,
    FILEGROWTH = 10%
)
TO FILEGROUP fg_orders_201101;

-- Add File for 2012
ALTER DATABASE advworks
ADD FILE 
(
    NAME = 'Partition2_File',
    FILENAME = 'C:\tmp\dummy\fg_orders_201201.ndf',
    SIZE = 100MB,
    MAXSIZE = UNLIMITED,
    FILEGROWTH = 10%
)
TO FILEGROUP fg_orders_201201;

-- Add File for 2013
ALTER DATABASE advworks
ADD FILE 
(
    NAME = 'Partition3_File',
    FILENAME = 'C:\tmp\dummy\fg_orders_201301.ndf',
    SIZE = 100MB,
    MAXSIZE = UNLIMITED,
    FILEGROWTH = 10%
)
TO FILEGROUP fg_orders_201301;
  • Each FILE command creates a new file in the specified filegroup, with growth settings and initial size defined.

4. Creating the Partition Scheme

The partition scheme maps partitions to filegroups. This scheme will use the previously created partition function and filegroups.

CREATE PARTITION SCHEME ps_orders_date_range  
AS PARTITION pf_orders_date_range  
TO (fg_orders_201101, fg_orders_201201, fg_orders_201301, [PRIMARY]);
  • ps_orders_date_range is the name of the partition scheme.
  • It maps the ranges defined in the partition function to the filegroups.

5. Creating the Partitioned Table

Finally, create the table and specify that it should use the partition scheme for data distribution.

CREATE TABLE [Sales].[SalesOrderHeaderPartitioned](
    [SalesOrderID] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
    [RevisionNumber] [tinyint] NOT NULL,
    [OrderDate] [datetime] NOT NULL,
    [DueDate] [datetime] NOT NULL,
    [ShipDate] [datetime] NULL,
    [Status] [tinyint] NOT NULL,
    [OnlineOrderFlag] [dbo].[Flag] NOT NULL,
    [SalesOrderNumber]  AS (isnull(N'SO'+CONVERT([nvarchar](23),[SalesOrderID]),N'*** ERROR ***')),
    [PurchaseOrderNumber] [dbo].[OrderNumber] NULL,
    [AccountNumber] [dbo].[AccountNumber] NULL,
    [CustomerID] [int] NOT NULL,
    [SalesPersonID] [int] NULL,
    [TerritoryID] [int] NULL,
    [BillToAddressID] [int] NOT NULL,
    [ShipToAddressID] [int] NOT NULL,
    [ShipMethodID] [int] NOT NULL,
    [CreditCardID] [int] NULL,
    [CreditCardApprovalCode] [varchar](15) NULL,
    [CurrencyRateID] [int] NULL,
    [SubTotal] [money] NOT NULL,
    [TaxAmt] [money] NOT NULL,
    [Freight] [money] NOT NULL,
    [TotalDue]  AS (isnull(([SubTotal]+[TaxAmt])+[Freight],(0))),
    [Comment] [nvarchar](128) NULL,
    [rowguid] [uniqueidentifier] ROWGUIDCOL  NOT NULL,
    [ModifiedDate] [datetime] NOT NULL,
    CONSTRAINT [PK_SalesOrderHeaderPartitioned_SalesOrderID] PRIMARY KEY CLUSTERED (
        [SalesOrderID] ASC,
        [OrderDate] ASC -- Include OrderDate in the primary key
    ) 
) ON ps_orders_date_range ([OrderDate]);
  • The ON ps_orders_date_range ([OrderDate]) clause specifies that the table uses the partition scheme, distributing data based on the OrderDate column.

6. Verifying the Partition Setup

To ensure that the partitions are correctly set up, you can run the following query:

SELECT 
    p.partition_number,
    f.name AS file_group,
    p.rows
FROM sys.partitions p
JOIN sys.destination_data_spaces dds ON p.partition_number = dds.destination_id
JOIN sys.filegroups f ON dds.data_space_id = f.data_space_id
WHERE OBJECT_ID = OBJECT_ID('Sales.SalesOrderHeaderPartitioned')
ORDER BY p.partition_number;
  • This query provides information about partition numbers, associated filegroups, and the number of rows in each partition.

Conclusion

Partitioning a table in SQL Server can significantly improve performance and ease data management. By following these steps—creating a partition function, adding filegroups and files, setting up a partition scheme, creating the partitioned table, and verifying the setup—you can efficiently manage large datasets and optimize query performance.