Table partitioning for large datasets

Maria Garcia Feb 2026
2 tabs
-- PostgreSQL Declarative Partitioning (10+)

-- Create partitioned table by date range
CREATE TABLE measurements (
  id BIGSERIAL,
  sensor_id INT NOT NULL,
  temperature DECIMAL(5,2),
  humidity DECIMAL(5,2),
  measured_at TIMESTAMP NOT NULL,
  PRIMARY KEY (id, measured_at)
) PARTITION BY RANGE (measured_at);

-- Create partitions for each month
CREATE TABLE measurements_2024_01 PARTITION OF measurements
  FOR VALUES FROM ('2024-01-01') TO ('2024-02-01');

CREATE TABLE measurements_2024_02 PARTITION OF measurements
  FOR VALUES FROM ('2024-02-01') TO ('2024-03-01');

CREATE TABLE measurements_2024_03 PARTITION OF measurements
  FOR VALUES FROM ('2024-03-01') TO ('2024-04-01');

-- Default partition for out-of-range values
CREATE TABLE measurements_default PARTITION OF measurements
  DEFAULT;

-- Query automatically uses partition pruning
EXPLAIN ANALYZE
SELECT * FROM measurements
WHERE measured_at >= '2024-02-01'
  AND measured_at < '2024-03-01';
-- Only scans measurements_2024_02 partition

-- Create indexes on partitions
CREATE INDEX idx_measurements_2024_01_sensor
  ON measurements_2024_01(sensor_id);

-- Attach existing table as partition
CREATE TABLE measurements_2023_12 (
  LIKE measurements INCLUDING ALL
);

ALTER TABLE measurements
  ATTACH PARTITION measurements_2023_12
  FOR VALUES FROM ('2023-12-01') TO ('2024-01-01');

-- Detach partition (for archival)
ALTER TABLE measurements
  DETACH PARTITION measurements_2023_12;

-- Archive to S3, then drop
DROP TABLE measurements_2023_12;

-- Partition by list (regions)
CREATE TABLE orders (
  id BIGSERIAL,
  customer_id INT,
  region VARCHAR(10),
  total DECIMAL(10,2),
  created_at TIMESTAMP,
  PRIMARY KEY (id, region)
) PARTITION BY LIST (region);

CREATE TABLE orders_us PARTITION OF orders
  FOR VALUES IN ('US', 'USA');

CREATE TABLE orders_eu PARTITION OF orders
  FOR VALUES IN ('UK', 'DE', 'FR', 'ES');

CREATE TABLE orders_asia PARTITION OF orders
  FOR VALUES IN ('JP', 'CN', 'IN');

-- Hash partitioning (even distribution)
CREATE TABLE users (
  id BIGSERIAL PRIMARY KEY,
  username VARCHAR(50),
  email VARCHAR(100)
) 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);

CREATE TABLE users_p2 PARTITION OF users
  FOR VALUES WITH (MODULUS 4, REMAINDER 2);

CREATE TABLE users_p3 PARTITION OF users
  FOR VALUES WITH (MODULUS 4, REMAINDER 3);
2 files · sql Explain with highlit

Partitioning splits large tables into smaller physical pieces. Range partitioning divides by value ranges—dates, IDs. List partitioning groups by specific values—regions, categories. Hash partitioning distributes evenly across partitions. I use partitioning for time-series data, archival strategies, query performance. Partition pruning scans only relevant partitions—dramatic speedup. Declarative partitioning in PostgreSQL 10+ simplifies management. Attach/detach partitions for efficient archival. Partitioned indexes improve query performance. Understanding partition key selection is critical—commonly queried columns. Partitioning enables dropping old data instantly. Balance partition count with maintenance overhead. Partitioning is essential for multi-terabyte tables and time-series workloads.