Time Partitions

QuestDB partitions tables by time intervals, storing each interval's data in a separate directory. This physical separation is fundamental to time-series performance - it allows the database to skip irrelevant time ranges entirely during queries and enables efficient data lifecycle management.

Why partition​

Partitioning provides significant benefits for time-series workloads:

  • Query performance: The SQL optimizer skips partitions outside your query's time range. A query for "last hour" on a table with years of data reads only one partition, not the entire table.
  • Data lifecycle: Drop old data instantly with DROP PARTITION - no expensive DELETE operations. Detach partitions to cold storage, reattach when needed.
  • Write efficiency: Out-of-order data only rewrites affected partitions, not the entire table. Smaller partitions mean less write amplification.
  • Concurrent access: Different partitions can be written and read simultaneously without contention.

How partitions work​

Partitioning requires a designated timestamp column. QuestDB uses this timestamp to determine which partition stores each row.

Diagram showing how table data is organized into time-based partition directories, each containing column files

Each partition is a directory on disk named by its time interval. Inside, each column is stored as a separate file (.d for data, plus index files for SYMBOL columns).

Choosing a partition interval​

Available intervals: HOUR, DAY, WEEK, MONTH, YEAR, or NONE.

Target 30-80 million rows per partition for tables with average-sized rows. Tables with many columns should aim for the lower end; tables with few columns can go higher.

Choose your interval based on how much data you ingest:

Your data volumeRecommended interval
>1 billion rows/dayHOUR
30-500 million rows/dayDAY
5-30 million rows/dayWEEK
1-5 million rows/dayMONTH
<1 million rows/dayYEAR

Why this matters:

  • Too many small partitions increases syscall overhead. Each partition is a directory, and operations like queries and compaction must interact with many filesystem objects.
  • Too few large partitions can hurt out-of-order write performance. When late data arrives, QuestDB may need to rewrite portions of the partition. Smaller partitions limit how much data gets rewritten in worst-case scenarios.

Other considerations:

  • Match your most common query patterns (if you typically query by day, DAY partitions align well)
  • You can change partitioning later, but it requires recreating the table

For ILP (InfluxDB Line Protocol) ingestion, the default is DAY. Change it via line.default.partition.by in server.conf.

Creating partitioned tables​

Specify partitioning at table creation:

CREATE TABLE trades (
ts TIMESTAMP,
symbol SYMBOL,
price DOUBLE,
amount DOUBLE
) TIMESTAMP(ts) PARTITION BY DAY;

Default behavior by creation method​

Creation methodDefault partition
SQL CREATE TABLE (no PARTITION BY)NONE
SQL CREATE TABLE (with PARTITION BY)As specified
ILP auto-created tablesDAY

Partition directory naming​

IntervalDirectory formatExample
HOURYYYY-MM-DDTHH2026-01-15T09
DAYYYYY-MM-DD2026-01-15
WEEKYYYY-Www2026-W03
MONTHYYYY-MM2026-01
YEARYYYY2026

Inspecting partitions​

Use SHOW PARTITIONS or the table_partitions() function:

SHOW PARTITIONS FROM trades;
indexpartitionBynameminTimestampmaxTimestampnumRowsdiskSizeHuman
0DAY2026-01-152026-01-15T00:00:00Z2026-01-15T23:59:59Z144000068.0 MiB
1DAY2026-01-162026-01-16T00:00:00Z2026-01-16T12:30:00Z75000035.2 MiB

The table_partitions() function returns the same data and can be used in queries with WHERE, JOIN, or UNION:

SELECT name, numRows, diskSizeHuman
FROM table_partitions('trades')
WHERE numRows > 1000000;

Storage on disk​

A partitioned table's directory structure:

db/trades/
├── 2026-01-15/ # Partition directory
│ ├── ts.d # Timestamp column data
│ ├── symbol.d # Symbol column data
│ ├── symbol.k # Symbol column index
│ ├── symbol.v # Symbol column values
│ ├── price.d # Price column data
│ └── amount.d # Amount column data
├── 2026-01-16/
│ ├── ts.d
│ ├── ...
└── _txn # Transaction metadata

Partition splitting and squashing​

When out-of-order data arrives for an existing partition, QuestDB may split that partition to avoid rewriting all its data. This is an optimization for write performance.

A split occurs when:

  • The existing partition prefix is larger than the new data plus suffix
  • The prefix exceeds cairo.o3.partition.split.min.size (default: 50MB)

Split partitions appear with timestamp suffixes in SHOW PARTITIONS:

namenumRows
2026-01-151259999
2026-01-15T205959-88000160002

QuestDB automatically squashes splits:

  • Non-active partitions: squashed at end of each commit
  • Active (latest) partition: squashed when splits exceed cairo.o3.last.partition.max.splits (default: 20)

To manually squash all splits:

ALTER TABLE trades SQUASH PARTITIONS;

Partition operations (ATTACH, DETACH, DROP) treat all splits of a partition as a single unit.

See also​