Redshift Partitioned Tables Explained: Spectrum, Partition Pruning, and Best Practices
Partitioned tables in Amazon Redshift let you split large S3 datasets into separate folder prefixes by date, region, or another dimension, so Redshift Spectrum reads only the data that can match your query. This guide explains what partitioned tables are, how partition pruning works, and how to set one up correctly from scratch.
What partitioned tables are
A partitioned table is a table whose underlying data is physically split into separate storage locations based on the values of one or more columns — the partition keys. When you run a query that filters on a partition key, the database skips storage locations that cannot contain matching rows. This is partition pruning.
In Redshift, partitioned tables are a Redshift Spectrum concept. You cannot partition a regular managed Redshift table. Partition pruning in Spectrum operates at the S3 folder level: whole folders are eliminated before Spectrum reads a single byte of data.
Sort keys are an entirely separate optimisation for internal managed tables. They sort rows on disk so that 1 MB blocks can be skipped using zone map metadata. Sort keys and partitioned tables apply to different table types and operate at different storage layers.
Think of an S3 data lake as a warehouse with millions of identical-looking boxes stacked floor to ceiling. Without partitions, finding last month’s sales data means opening every box. Partitions are labelled bays: all boxes from June 2024 sit in bay 6, all boxes from July in bay 7. When you need June, you walk straight to that bay and ignore the other 11. Partition pruning is what closes the doors to the bays you don’t need.
What “partitioned tables” means in Redshift
Amazon Redshift has two modes of querying data: managed tables (data loaded into Redshift’s own columnar storage) and external tables via Redshift Spectrum (data queried directly from S3 without loading it first). Partitioned tables are an external table feature.
External tables are defined in the AWS Glue Data Catalog, which acts as a metadata store. The actual rows live in S3. When you define an external table as partitioned, the Glue catalog stores the mapping from each partition key value to the corresponding S3 folder prefix. Spectrum reads those mappings at query time to decide which folders to open.
This pattern is central to data lake architectures on AWS: raw data lands in S3 in organised prefixes, and you query it in place without the cost and latency of a full COPY load into Redshift’s managed storage.
How partition pruning works
When Redshift Spectrum executes a query against an external table, it first checks the Glue
Data Catalog for the list of registered partitions. If your query includes a WHERE clause
on a partition column such as WHERE year = 2024 AND month = 6, Spectrum
eliminates every partition whose key values don’t match. Only the matching S3 folders are
opened and read.
Partition pruning works like a library catalogue. When you search for books by a specific author, the catalogue tells you which aisle to walk to. You don’t scan every shelf in the building. Spectrum uses the Glue Data Catalog the same way: before reading a single file from S3, it checks which folder prefixes could possibly contain your data, then reads only those. Everything else is invisible to the query.
This matters because Redshift Spectrum charges $5 per terabyte scanned from S3. A table with two years of daily partitions has over 700 partitions. A query filtered to a single month prunes roughly 96% of partitions and 96% of the scan cost before a single file is opened.
A query with no WHERE clause on a partition column forces Spectrum to scan every registered partition. On a two-year dataset with daily partitions, that means opening 700+ folders, potentially reading terabytes of data, and billing accordingly. This surprises beginners who assume “it’s just a query.” Always include a partition filter in production queries on large external tables.
Run EXPLAIN before your SELECT to check whether pruning is active. Look
for partition scan statistics in the Spectrum node output — it will show how many total
partitions exist versus how many will actually be scanned. If all partitions are being
scanned, check whether your WHERE clause references a partition column and whether the
data type of your filter value matches the partition column type.
A practical example from scratch
Step 1 — Organise S3 data using Hive-style paths
Data written into S3 using Hive-style key=value folder names is recognised
automatically by Glue crawlers, removing the need to register partitions by hand. A
typical layout for a daily events dataset:
s3://my-data-lake/app-events/year=2024/month=01/day=01/events.parquet
s3://my-data-lake/app-events/year=2024/month=01/day=02/events.parquet
s3://my-data-lake/app-events/year=2024/month=02/day=01/events.parquet
s3://my-data-lake/app-events/year=2025/month=01/day=01/events.parquetStep 2 — Create an external schema pointing to Glue
Before creating external tables, create an external schema in Redshift that maps to a Glue Data Catalog database. The IAM role attached to the cluster needs S3 read and Glue catalog access:
CREATE EXTERNAL SCHEMA spectrum
FROM DATA CATALOG
DATABASE 'analytics_db'
IAM_ROLE 'arn:aws:iam::123456789012:role/RedshiftSpectrumRole'
CREATE EXTERNAL DATABASE IF NOT EXISTS;Step 3 — Create the partitioned external table
Partition columns go in the PARTITIONED BY clause, not in the main column
list. They are derived from the folder path at query time, not stored in the data files:
CREATE EXTERNAL TABLE spectrum.app_events (
event_id BIGINT,
user_id BIGINT,
event_type VARCHAR(50),
page_url VARCHAR(500)
)
PARTITIONED BY (year INT, month INT, day INT)
STORED AS PARQUET
LOCATION 's3://my-data-lake/app-events/';Step 4 — Register a partition
After writing data to a new S3 folder, register the partition in the Glue catalog. Without this step, the data exists in S3 but is completely invisible to queries:
ALTER TABLE spectrum.app_events
ADD PARTITION (year=2024, month=1, day=1)
LOCATION 's3://my-data-lake/app-events/year=2024/month=1/day=1/';If you write new data to S3 but forget to register the partition, Spectrum queries return zero rows for that period without raising any error. There is no warning. Your dashboard shows a flat line for the missing dates, and it looks like there was no activity. This is one of the most common and hardest-to-spot mistakes in a Spectrum setup. Treat partition registration as a required step in your load pipeline, not an afterthought.
Step 5 — Query with a partition filter
A query that filters on partition columns triggers pruning. The query below reads only the June 2024 folders. All other year and month partitions are skipped:
SELECT
event_type,
COUNT(*) AS event_count
FROM spectrum.app_events
WHERE year = 2024
AND month = 6
GROUP BY event_type
ORDER BY event_count DESC;When to use partitioned tables
- Large data lakes in S3. Any Spectrum external table over a few GB benefits from partitioning, as long as queries include a filter on the partition column.
- Time-series and event data. Application logs, clickstream events, IoT telemetry, and audit trails almost always include a date dimension. Year, month, and day are natural partition keys for this data.
- Log archives. Historical log data that you query occasionally but rarely need in full is a textbook case. Partition by date and queries for a specific period only read that period’s folders.
- Multi-tenant data. If tenant or region data lives in separate S3 folders for access-control or billing reasons, partitioning by that dimension gives you folder-level isolation with no query overhead when filtering by it.
When not to use partitioned tables
- Small tables. A dataset under a few hundred MB scans in seconds regardless of partitioning. Managing partition registration adds complexity with no meaningful return.
- Queries that never filter by the partition column. If every query does a full table scan with no date filter, partition pruning never fires and you still pay to scan the full dataset.
- High-cardinality partition keys. Partitioning by user_id, session_id, or any column with millions of distinct values creates millions of tiny partitions. Glue catalog metadata overhead and S3 small-file proliferation hurt performance rather than help it.
- Data that belongs in managed Redshift storage. If your working dataset is small enough to load into Redshift’s columnar storage, load it there instead. Sort keys and distribution keys will outperform Spectrum for that use case. See Loading Data into Redshift for when COPY is the better choice.
Choosing a good partition key
Time-based keys
Year, month, and day are the most common partition keys because most analytical queries include a date range filter. Partitioning at the day level works well when queries typically target a week or month of data. Partitioning at the month level is sufficient if your queries never narrow below a full month.
Cardinality and granularity
The right granularity is the coarsest level at which pruning still removes meaningful data. If every query spans at least a full day, partitioning by hour adds 24x the number of partitions without improving how much data gets skipped.
Very fine-grained partitioning also creates practical problems:
- Thousands of small files mean slow S3 LIST operations and Glue catalog lookups
- Parquet and ORC files below roughly 128 MB compress and encode poorly because there are not enough rows per file for the columnar format to be efficient
- Redshift Spectrum has a default limit of 10,000 partitions scanned per query, and highly partitioned tables can hit this ceiling
By default, Redshift Spectrum will not scan more than 10,000 partitions in a single query. If your table has more partitions than that and you run a query without a strong enough partition filter, Spectrum returns an error rather than a partial result. You can request a limit increase from AWS Support, but hitting this limit usually signals that your partitioning granularity is too fine.
Low vs high cardinality
Good partition keys have low-to-moderate cardinality: year (a handful of values), month (12 per year), region (5 to 20 values). Columns with millions of distinct values such as order IDs or hashed identifiers are poor partition keys because the metadata overhead outweighs any pruning benefit.
Partitioned tables vs sort keys
Both can speed up date-range queries, which is why beginners often conflate them. They operate at completely different layers and are not interchangeable. For a full explanation of sort keys, VACUUM, and managed table tuning, see Redshift Performance Optimisation.
| Factor | Partitioned tables (Spectrum) | Sort keys (managed tables) |
|---|---|---|
| Where data lives | S3, queried via Redshift Spectrum | Redshift managed columnar storage |
| What is skipped | Entire S3 folders | 1 MB disk blocks via zone maps |
| Pruning granularity | Folder-level — can skip millions of rows at once | Block-level — up to ~250k rows per block |
| Setup | PARTITIONED BY clause plus register partitions | SORTKEY clause on CREATE TABLE |
| Ongoing maintenance | Register new partitions after each data load | Run VACUUM after large loads |
| Cost impact | Directly reduces Spectrum per-TB scan charge | Reduces compute time; no per-scan charge |
| Can they be combined? | Not on the same table. Use partitions for Spectrum external tables, sort keys for managed tables. | |
Common beginner mistakes
- Forgetting to register new partitions. Data written to a new S3 folder is invisible to Redshift Spectrum until the partition is registered via ALTER TABLE ADD PARTITION or a Glue crawler run. Queries return zero rows for recent data without raising an error, which makes this easy to miss for days.
- Querying without a partition filter. A SELECT with no WHERE clause on a partition column forces Spectrum to scan every registered partition. On a two-year dataset with daily partitions, that is 700+ S3 folders and potentially terabytes of billed data. Always include a partition filter in production queries on large tables.
- Partitioning by a high-cardinality column. Using user_id, session_id, or any column with millions of distinct values as a partition key creates millions of tiny partitions. Glue catalog overhead becomes significant and S3 contains thousands of files too small to compress efficiently, making queries slower rather than faster.
- Treating partitioned tables and sort keys as the same thing. Sort keys optimise managed Redshift tables. Partitioned tables optimise Spectrum external tables on S3. These are different mechanisms that apply to different table types. Applying sort key logic to a Spectrum table, or expecting partition pruning on a managed table, will not produce the expected result.
- Using CSV instead of a columnar format. Partition pruning skips folders, but once Spectrum opens a file, the format determines how efficiently it reads it. CSV requires reading every column even if you SELECT only two. Parquet and ORC support column projection, so Spectrum transfers only the columns your query actually needs. Using CSV on a partitioned table leaves significant performance and cost savings untouched.
Best practices
- Use Hive-style partition paths. Writing data to
year=2024/month=06/day=15/prefixes means Glue crawlers can auto-discover and register new partitions without manual ALTER TABLE calls. Schedule the crawler to run after each data load and the catalog stays in sync automatically. - Match partition granularity to your actual query patterns. If no query ever filters below the month level, partitioning by day adds 30x the number of partitions without any additional pruning benefit. Start at a coarser level and add granularity only if you have a specific need.
- Store data in Parquet or ORC. Both are columnar formats with built-in compression. They compound the savings from partition pruning: pruning skips folders, and column projection reduces the bytes read within the folders that remain. See ETL vs ELT for how a Glue job can convert CSV source data into Parquet as part of your load pipeline.
- Treat partition registration as a required pipeline step. Whether you use a Glue crawler, a Lambda trigger, or an ALTER TABLE call in a Step Functions workflow, register partitions immediately after writing data to S3. It is not optional.
- Monitor Spectrum scan volume. Query
SVL_S3QUERY_SUMMARYin Redshift to see bytes scanned per Spectrum query. Unexpectedly high scan bytes almost always means a missing partition filter or a type mismatch between the filter value and the partition column type. This connects directly to Redshift pricing, where Spectrum scan costs are the most variable component of your bill. - Decide upfront whether data belongs in Spectrum or managed storage. Querying the same dataset via Spectrum and also loading it into managed Redshift doubles your storage cost. The data warehouse vs data lake framing helps: large historical archives stay in S3 as Spectrum external tables; active working data gets loaded into managed storage where sort keys and distribution keys apply.
Summary
- Partitioned tables in Redshift are external tables (Redshift Spectrum) whose data is split into separate S3 folder prefixes by partition key values
- Partition pruning skips S3 folders that cannot match a query’s WHERE clause, reducing data scanned, query time, and Spectrum cost
- New partitions must be registered in the Glue Data Catalog before Spectrum can read them — either manually with ALTER TABLE ADD PARTITION or automatically via a Glue crawler
- Partition keys should have low-to-moderate cardinality; date-based keys (year, month, day) are the most common and practical choice
- Partitioned tables and sort keys are unrelated — sort keys are for internal managed Redshift tables, partitions are for Spectrum external tables on S3
- Combining partition pruning with Parquet or ORC storage gives the best cost and query performance outcome
Frequently asked questions
What is a partitioned table in Redshift?
A partitioned table in Redshift is an external table (used with Redshift Spectrum) whose data is split into separate S3 folder prefixes based on one or more partition columns. When you query with a filter on a partition column, Redshift Spectrum only reads the matching folders, skipping all others. This is called partition pruning.
What is partition pruning in Redshift Spectrum?
Partition pruning is the process of skipping S3 folders that cannot contain rows matching your query filter. If your external table is partitioned by year and month, a query with WHERE year = 2024 AND month = 6 only scans June 2024 data. Folders for all other months are not opened at all. This directly reduces the amount of data Spectrum reads from S3, lowering both query time and the per-TB cost Redshift Spectrum charges.
How are partitioned tables different from sort keys in Redshift?
Partitioned tables and sort keys solve different problems at different layers. Partitioned tables apply to external tables (Redshift Spectrum) on S3 — they skip entire S3 folders at query time. Sort keys apply to internal managed Redshift tables and sort rows on disk so Redshift can skip 1 MB blocks using zone maps. You cannot partition an internal Redshift table, and sort keys have no effect on Spectrum external tables.
Do I need to register partitions manually?
It depends on how you load data. If you write data into S3 using Hive-style paths (e.g., year=2024/month=6/day=15/), an AWS Glue crawler can auto-discover and register new partitions each time it runs. If you manage loading yourself without a crawler, you must run ALTER TABLE ... ADD PARTITION for each new partition before Redshift Spectrum can read it.
Does Redshift Spectrum charge based on data scanned?
Yes. Redshift Spectrum charges $5 per terabyte of data scanned from S3. Partition pruning directly reduces this cost by limiting which S3 folders Spectrum reads. Combining partition pruning with a columnar format like Parquet — which lets Spectrum read only the columns you SELECT — is the most effective way to control Spectrum query costs.