Working with Table Partitioning
1. Creating Partitioned Table
CREATE TABLE events (
id bigserial,
tenant_id int NOT NULL,
created_at timestamptz NOT NULL,
payload jsonb,
PRIMARY KEY (id, created_at)
) PARTITION BY RANGE (created_at);
2. Using Range Partitioning
CREATE TABLE events_2025_01 PARTITION OF events
FOR VALUES FROM ('2025-01-01') TO ('2025-02-01');
3. Using List Partitioning
CREATE TABLE orders (...) PARTITION BY LIST (region);
CREATE TABLE orders_us PARTITION OF orders FOR VALUES IN ('US','CA');
CREATE TABLE orders_eu PARTITION OF orders FOR VALUES IN ('DE','FR','GB');
4. Using Hash Partitioning
CREATE TABLE users (...) PARTITION BY HASH (id);
CREATE TABLE users_p0 PARTITION OF users FOR VALUES WITH (MODULUS 4, REMAINDER 0);
CREATE TABLE users_p1 PARTITION OF users FOR VALUES WITH (MODULUS 4, REMAINDER 1);
| Strategy | Best For |
|---|---|
| RANGE | Time-series, ordinal keys |
| LIST | Discrete categories (region, tenant) |
| HASH | Even data spread, multitenancy |
5. Creating Partitions
CREATE TABLE events_2025_02 PARTITION OF events
FOR VALUES FROM ('2025-02-01') TO ('2025-03-01')
TABLESPACE fast_ssd;
6. Setting Range Bounds
FOR VALUES FROM (MINVALUE) TO ('2024-01-01');
FOR VALUES FROM ('2025-01-01') TO (MAXVALUE);
7. Setting List Values
FOR VALUES IN ('a','b','c');
8. Creating Default Partition
CREATE TABLE events_default PARTITION OF events DEFAULT;
Warning: Adding a new partition requires scanning the default partition for misplaced rows; keep it empty if possible.
9. Attaching Existing Table
ALTER TABLE events ATTACH PARTITION events_2024
FOR VALUES FROM ('2024-01-01') TO ('2025-01-01');
10. Detaching Partition
ALTER TABLE events DETACH PARTITION events_2023 CONCURRENTLY;
-- PG 14+ avoids ACCESS EXCLUSIVE on the parent
11. Dropping Partition
DROP TABLE events_2022; -- after DETACH (fast purge of old data)
12. Creating Indexes on Partitioned Tables
CREATE INDEX events_tenant_created_idx
ON events (tenant_id, created_at DESC);
-- Cascades to all current/future partitions as partitioned index
13. Using Partition Pruning
SET enable_partition_pruning = on; -- default
EXPLAIN SELECT * FROM events
WHERE created_at >= '2025-02-15' AND created_at < '2025-02-20';
-- Plan: Append scans only events_2025_02
14. Using Partition-wise Join
SET enable_partitionwise_join = on;
15. Using Partition-wise Aggregate
SET enable_partitionwise_aggregate = on;
| Setting | Effect |
|---|---|
| partition_pruning | Skip non-matching partitions |
| partitionwise_join | Join matching partitions in parallel |
| partitionwise_aggregate | Aggregate per partition then combine |