Table Partitioning
Split a huge table into partitions by a key such as match date using a partition function, filegroups, data files and a partition scheme.
Imagine an innings table holding every innings of every IPL season — hundreds of millions of rows. Most queries only care about one season, yet every query, index rebuild and backup has to deal with the whole monster. Partitioning cuts it into smaller pieces (one per season) while your queries still just say FROM innings.
Definition
Partitioning divides one large table (or index) into smaller, separately stored pieces called partitions, based on the value of a partition key column, while the table is still treated as ONE logical table by every query.
- Partition key — the column that decides which partition a row belongs to, e.g.
match_dateorregion. - Separately stored — each partition can live in its own filegroup, even on a different disk.
- One logical table — no query changes:
SELECT … FROM innings WHERE …works exactly as before.
Why do we need partitions?
| Benefit | How |
|---|---|
| Faster queries | WHERE match_date >= '2025-01-01' touches only the 2025 partition — called partition elimination |
| Parallel work | several CPUs read different partitions at the same time (parallel processing) |
| Easier maintenance | rebuild indexes one partition at a time (per-partition index) |
| Archive old data | switch out the 2022 partition instantly and load the new season (old data / new data) |
The 4 building blocks
| Step | Object | What it does | Command |
|---|---|---|---|
| 1 | Partition function | defines the logic — the boundary values where one partition ends and the next begins | CREATE PARTITION FUNCTION |
| 2 | Filegroups | logical containers that organise where partitions are stored | ALTER DATABASE … ADD FILEGROUP |
| 3 | Data files | the physical .ndf files on disk, added to each filegroup | ALTER DATABASE … ADD FILE |
| 4 | Partition scheme | maps each partition to a filegroup | CREATE PARTITION SCHEME |
Finally you create the table ON the partition scheme, naming the partition key column.
Partition function — RANGE LEFT vs RANGE RIGHT
Boundaries 2022-12-31 | 2023-12-31 | 2024-12-31 → 3 boundaries make 4 partitions:
| Partition | RANGE LEFT holds |
|---|---|
| 1 | 2022 and earlier (≤ 2022-12-31) |
| 2 | 2023 (> 2022-12-31 and ≤ 2023-12-31) |
| 3 | 2024 |
| 4 | 2025 onwards |
RANGE LEFT — a boundary value belongs to the partition on its LEFT (2023-12-31 stays in partition 2). RANGE RIGHT — a boundary value starts the partition on its RIGHT (2023-12-31 would start partition 3).
Filegroups and data files
A filegroup is a logical container of one or more data files. Data files (
.ndf) are the physical files on disk.
| Filegroup | Partition | Data files |
|---|---|---|
| FG_2022 | Partition 1 | file1a.ndf, file1b.ndf |
| FG_2023 | Partition 2 | file2a.ndf, file2b.ndf |
| FG_2024 | Partition 3 | file3a.ndf, file3b.ndf |
| FG_2025 | Partition 4 | file4a.ndf, file4b.ndf |
Each partition can live in its own filegroup (even on different disks); a filegroup can hold several data files.
Syntax — full example
sql
-- 1. Partition function: the boundaries
CREATE PARTITION FUNCTION pf_season (DATE)
AS RANGE LEFT FOR VALUES ('2022-12-31', '2023-12-31', '2024-12-31');
-- 2 + 3. Filegroups and data files (repeat for FG_2023, FG_2024, FG_2025)
ALTER DATABASE IPL ADD FILEGROUP FG_2022;
ALTER DATABASE IPL ADD FILE
(NAME = 'ipl_2022', FILENAME = 'D:\SQLData\ipl_2022.ndf')
TO FILEGROUP FG_2022;
-- 4. Partition scheme: partition -> filegroup
CREATE PARTITION SCHEME ps_season
AS PARTITION pf_season
TO (FG_2022, FG_2023, FG_2024, FG_2025);
-- Table stored ON the scheme, using match_date as the partition key
CREATE TABLE innings (
id INT,
player VARCHAR(50),
match_date DATE,
runs INT
) ON ps_season (match_date);| Piece | Meaning |
|---|---|
(DATE) | data type of the partition key |
RANGE LEFT FOR VALUES (…) | boundary values; LEFT/RIGHT decides where a boundary value goes |
TO (FG_2022, …) | one filegroup per partition, in order (ALL TO ([PRIMARY]) puts all in one) |
ON ps_season (match_date) | store the table on the scheme, split by match_date |
sql
-- Which partition does each row live in? How many rows per partition?
SELECT $PARTITION.pf_season(match_date) AS partition_no, COUNT(*) AS rows_in_partition
FROM innings
GROUP BY $PARTITION.pf_season(match_date);
SELECT partition_number, rows
FROM sys.partitions
WHERE object_id = OBJECT_ID('innings');Example — simulate RANGE LEFT vs RANGE RIGHT (playground)
The playground cannot partition tables, but a CASE expression can show exactly which partition each row would go to:
| player | match_date | range_left_partition | range_right_partition |
|---|---|---|---|
| Virat Kohli | 2022-05-18 | 1 | 1 |
| Jos Buttler | 2022-05-27 | 1 | 1 |
| Shubman Gill | 2023-05-26 | 2 | 2 |
| Boundary Day | 2023-12-31 | 2 | 3 |
| Jos Buttler | 2024-04-16 | 3 | 3 |
| Virat Kohli | 2024-04-25 | 3 | 3 |
| Rohit Sharma | 2025-04-20 | 4 | 4 |
Only the row that falls exactly on a boundary differs: LEFT keeps it in partition 2, RIGHT moves it to partition 3. (Run the first example below to see the query.)
Partitioning + indexing
For very large fact tables, the best option is both: partition by season and add a columnstore index. Each partition gets its own portion of the index, so:
- Partition = read only the season you need.
- Columnstore = compress and scan fast inside it.
- You can rebuild one season at a time.
Common mistakes
- ❌ Partitioning a small table "for speed". ✅ Partitioning is for very large tables (tens of millions of rows+); on small tables it only adds complexity.
- ❌ Filtering on a different column than the partition key. ✅ Partition elimination only happens when the
WHEREuses the partition key. - ❌ Getting LEFT/RIGHT wrong for dates. ✅ With RANGE LEFT use the last day of each period (
2023-12-31); with RANGE RIGHT use the first day (2024-01-01). - ❌ Forgetting that N boundaries make N + 1 partitions (and need N + 1 filegroups in the scheme).
Interview tip
Q: Does partitioning always make queries faster? A: No. It helps when queries filter on the partition key (partition elimination) and with maintenance and archiving. Queries that don't filter on the key may even get slower because they must touch every partition. Its biggest wins are often manageability: per-partition rebuilds and instant switch-in/switch-out.
Key points to remember
- Partitioning splits one big table into smaller partitions by a partition key, but it is still ONE logical table to queries.
- Benefits: partition elimination (faster queries), parallel processing, per-partition maintenance, and instant archiving by switching partitions.
- Four building blocks: partition function (boundaries) → filegroups (logical containers) → data files (.ndf) → partition scheme (partition → filegroup).
- N boundary values create N + 1 partitions.
- RANGE LEFT puts a boundary value in the left partition; RANGE RIGHT makes it start the right partition.
- The table is created ON the partition scheme with the key column, e.g. ON ps_season (match_date).
- For huge fact tables, combine partitioning with a columnstore index.
Try it yourself
Try it: which partition would each row go to?
Runs on a fresh IPL practice database in your browser · edit the query and press Ctrl + Enter
SQL Server: full partitioning script
-- 1. Partition function
CREATE PARTITION FUNCTION pf_season (DATE)
AS RANGE LEFT FOR VALUES ('2022-12-31', '2023-12-31', '2024-12-31');
-- 2. Filegroups
ALTER DATABASE IPL ADD FILEGROUP FG_2022;
ALTER DATABASE IPL ADD FILEGROUP FG_2023;
ALTER DATABASE IPL ADD FILEGROUP FG_2024;
ALTER DATABASE IPL ADD FILEGROUP FG_2025;
-- 3. Data files (one shown; repeat for each filegroup)
ALTER DATABASE IPL ADD FILE
(NAME = 'ipl_2022', FILENAME = 'D:\SQLData\ipl_2022.ndf')
TO FILEGROUP FG_2022;
-- 4. Partition scheme
CREATE PARTITION SCHEME ps_season
AS PARTITION pf_season
TO (FG_2022, FG_2023, FG_2024, FG_2025);
-- Table on the scheme
CREATE TABLE innings (
id INT,
player VARCHAR(50),
match_date DATE,
runs INT
) ON ps_season (match_date);
-- Check the partitions
SELECT partition_number, rows
FROM sys.partitions
WHERE object_id = OBJECT_ID('innings');SQL Server-specific — run this in SQL Server (SSMS / Azure Data Studio).
SQL Server: partition + columnstore
-- Best option for huge fact tables:
-- partition by season, then compress each partition column by column
CREATE CLUSTERED COLUMNSTORE INDEX CCI_innings
ON innings
ON ps_season (match_date);SQL Server-specific — run this in SQL Server (SSMS / Azure Data Studio).
Practice: change the boundaries
Runs on a fresh IPL practice database in your browser · edit the query and press Ctrl + Enter
Revision Flashcards
Tap a card to flip it and see the answer.
