PostgreSQL Horizontal and Vertical Partitioning Explained
Horizontal partitioning divides a table’s rows among smaller child tables, while vertical partitioning separates its columns into related tables. PostgreSQL supports declarative table partitioning for the horizontal form; vertical partitioning is generally designed with ordinary tables, keys, and queries rather than a built-in vertical-partitioning feature.
What horizontal and vertical partitioning mean
Partitioning is a way to organize data so that parts of a logical dataset can be managed separately. The terms describe two different axes:
- Horizontal partitioning (also called row partitioning or sharding in some contexts) assigns different rows to different partitions. Each partition has the same columns, but holds only rows matching its boundary or partition key.
- Vertical partitioning assigns different columns to separate tables. Tables usually share a primary key, allowing a query to join the pieces when it needs them together.
These approaches address different needs. Horizontal partitioning can help manage very large tables and operations on subsets of rows. Vertical partitioning can keep frequently used columns apart from large or infrequently accessed fields. Neither automatically makes every query faster, and neither replaces sound indexing, schema design, or capacity planning.
How PostgreSQL horizontal partitioning works
In declarative partitioning, a parent table defines the shared columns and partitioning rule. It does not store rows itself when configured as a partitioned table; rows are stored in child tables, or partitions. PostgreSQL routes inserts to a matching partition and can use partition pruning to avoid scanning partitions that cannot contain rows relevant to a query.
PostgreSQL provides three main partitioning methods:
- Range: partitions cover value ranges, often dates. For example, an events table can have monthly partitions with boundaries such as January 1 up to, but not including, February 1.
- List: partitions contain explicitly listed values, such as rows grouped by region or status.
- Hash: PostgreSQL hashes the partition key and distributes rows across a chosen number of partitions. This can spread data evenly, but does not naturally group a meaningful range such as a month.
A simplified range example is:
CREATE TABLE events (
event_id bigint,
occurred_at timestamptz NOT NULL,
payload jsonb
) PARTITION BY RANGE (occurred_at);
CREATE TABLE events_2026_01 PARTITION OF events
FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');
The upper range bound is exclusive. A row whose timestamp falls outside all defined ranges cannot be inserted unless a suitable partition exists or a default partition has been configured. The partition key and boundaries therefore need to reflect the data and insert patterns.
How vertical partitioning works
Vertical partitioning separates columns according to how they are used. For instance, an account table might keep identifiers and frequently displayed profile fields together, while a separate account_details table stores a large biography or rarely requested preferences:
CREATE TABLE account (
account_id bigint PRIMARY KEY,
display_name text NOT NULL
);
CREATE TABLE account_details (
account_id bigint PRIMARY KEY REFERENCES account(account_id),
biography text
);
The shared key establishes the relationship. A query needing both sets of fields joins the tables; a query that only needs display names can avoid reading the biography column. This can improve practical data access patterns, but it also adds joins and application or schema complexity. PostgreSQL does not transparently split and reassemble arbitrary columns in the way declarative partitioning routes rows.
Vertical design can also arise naturally through normalization: related but distinct facts belong in separate tables to represent relationships and avoid duplicated data. That is a modeling decision, not necessarily a performance optimization.
Choosing a partitioning strategy
Start with the workload and operational problem, not the assumption that a large table must be partitioned. Horizontal partitioning is most useful when queries commonly filter on the partition key, or when managing a subset independently matters. A time-based range scheme can make it easier to detach or remove old data by partition rather than deleting millions of rows individually, subject to the application’s retention requirements.
Vertical partitioning may fit when a small, frequently queried set of columns is used far more often than bulky or optional fields, or when different column groups have distinct access patterns. Before splitting a table, check whether indexes, query changes, or storing large values appropriately would solve the problem with less complexity.
- Use range partitions when queries and maintenance naturally follow ordered ranges such as dates.
- Use list partitions when a small set of known categories needs separate handling.
- Consider hash partitions when distributing rows evenly is more useful than range-based access or lifecycle management.
- Consider vertical separation when many queries do not need certain columns and the join cost is acceptable.
Partitioning is not the same as distributing data across multiple servers. PostgreSQL table partitions are managed within a database and do not by themselves provide horizontal scaling across machines.
Indexes, constraints, and common pitfalls
Partitioning changes how database objects and constraints behave. A primary key or unique constraint declared on a partitioned table must include all columns used in its partition key; PostgreSQL needs that information to enforce uniqueness across the partitioned structure. A unique index on a partitioned table likewise has partition-key considerations. Confirm the exact design against the PostgreSQL version in use.
Indexes created on a partitioned parent are represented by corresponding indexes on its partitions. Indexes still need to match actual query patterns; partitioning does not make an unindexed lookup efficient by itself. Also, too many small partitions can increase planning and management overhead. Choose a partition count and granularity that suit expected data volume and maintenance cadence.
Common problems include missing partitions for new incoming values, partition bounds that do not match timestamp or timezone assumptions, and queries that fail to filter on the partition key and therefore scan many partitions. Verify pruning with EXPLAIN or EXPLAIN ANALYZE using representative queries and data. Remember that partition pruning depends on the query and planning conditions; it is not guaranteed merely because a table is partitioned.
For vertical splits, watch for extra joins, inconsistent one-to-one relationships, and application code that assumes all fields are in one table. A foreign key from the detail table to the main table helps prevent orphaned details; a matching primary key ensures at most one detail row per account.
Performance and operational trade-offs
Horizontal partitions can simplify bulk retention work and reduce the data considered by some queries. They can also make maintenance more targeted. However, partitions do not shrink the total amount of data, eliminate the need for backups, or guarantee faster inserts. Inserts still require a matching partition, and queries spanning many partitions may see little benefit.
Vertical partitioning can reduce the columns read for common queries and make wide rows less costly to handle in some workloads. The trade-off is more joins, more objects to maintain, and potentially more complicated writes and transactions. Measure with realistic queries, data distributions, and indexes before and after a change. PostgreSQL behavior and available features can differ by major version, so consult documentation for the deployed version when planning constraints, partition maintenance, or migration procedures.
Frequently Asked Questions
Does PostgreSQL support vertical partitioning?
PostgreSQL does not provide a declarative vertical-partitioning command. You can model it with related tables that share a key, then join them when a query needs columns from both.
Does partitioning automatically make a PostgreSQL table faster?
No. It can help when queries or maintenance can target a subset of partitions, but poorly matched partition keys, broad queries, or excessive partitions can reduce or erase the benefit.
What is the difference between partitioning and sharding?
Partitioning divides a table into logical pieces within PostgreSQL’s partitioned-table structure. Sharding usually means distributing data across separate database instances or servers; PostgreSQL partitioning alone does not do that.
Can I partition a table by date?
Yes. Range partitioning is commonly used for dates or timestamps. Define non-overlapping boundaries, plan ahead for future values, and account for timestamp and timezone handling in the application.
When should I split a table vertically?
Consider it when certain columns are large or rarely needed and common queries can avoid them, or when the data model calls for separate related entities. Compare the benefit with the added joins and consistency requirements.
Related
Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.