TimescaleDB Cheat Sheet
TimescaleDB time-series extension for Postgres covering hypertables, continuous aggregates, compression, and retention policies.
Creating a Hypertable
Convert a regular Postgres table into a time-partitioned hypertable.
CREATE TABLE metrics ( time TIMESTAMPTZ NOT NULL, device_id TEXT NOT NULL, cpu_pct DOUBLE PRECISION, mem_pct DOUBLE PRECISION);SELECT create_hypertable('metrics', by_range('time'));-- Optional: space-partition by device as wellSELECT add_dimension('metrics', by_hash('device_id', 4));CREATE INDEX ON metrics (device_id, time DESC);
Continuous Aggregate
Incrementally materialized rollups that stay fresh via a background policy.
CREATE MATERIALIZED VIEW metrics_hourlyWITH (timescaledb.continuous) ASSELECT time_bucket('1 hour', time) AS bucket, device_id, avg(cpu_pct) AS avg_cpu, max(cpu_pct) AS max_cpuFROM metricsGROUP BY bucket, device_id;-- Keep it refreshed automaticallySELECT add_continuous_aggregate_policy('metrics_hourly', start_offset => INTERVAL '3 hours', end_offset => INTERVAL '1 hour', schedule_interval => INTERVAL '1 hour');
Compression & Retention Policies
Automatically compress old chunks and drop data past a retention window.
ALTER TABLE metrics SET ( timescaledb.compress, timescaledb.compress_segmentby = 'device_id', timescaledb.compress_orderby = 'time DESC');SELECT add_compression_policy('metrics', INTERVAL '7 days');SELECT add_retention_policy('metrics', INTERVAL '180 days');-- time_bucket_gapfill for dashboards with missing intervalsSELECT time_bucket_gapfill('1 hour', time) AS bucket, device_id, interpolate(avg(cpu_pct))FROM metricsWHERE time > now() - INTERVAL '1 day'GROUP BY bucket, device_id;
Key Functions
TimescaleDB-specific SQL functions you'll use constantly.
- time_bucket(interval, ts)- buckets timestamps into fixed intervals, the core of any rollup query
- create_hypertable()- converts a plain table into an auto-partitioned hypertable
- add_continuous_aggregate_policy()- schedules automatic incremental refresh of a continuous aggregate
- add_compression_policy()- compresses chunks older than a threshold, often 10-20x storage reduction
- show_chunks() / drop_chunks()- inspect or manually drop chunks by age, useful for manual retention control
- locf() / interpolate()- gap-filling functions for dashboards over sparse time-series
Approximate Percentiles with Toolkit Hyperfunctions
Use timescaledb_toolkit's percentile_agg to compute accurate approximate percentiles over continuous aggregates instead of expensive PERCENTILE_CONT scans.
CREATE EXTENSION IF NOT EXISTS timescaledb_toolkit;CREATE MATERIALIZED VIEW cpu_percentilesWITH (timescaledb.continuous) ASSELECT time_bucket('1 hour', time) AS bucket, device_id, percentile_agg(cpu_pct) AS cpu_distFROM metricsGROUP BY bucket, device_id;SELECT bucket, device_id, approx_percentile(0.95, cpu_dist) AS p95_cpu, approx_percentile(0.50, cpu_dist) AS median_cpuFROM cpu_percentilesWHERE bucket > now() - INTERVAL '1 day';
Chunk Sizing & Inspection
Tune chunk_time_interval and inspect per-chunk metadata to keep chunks in the recommended 25% of RAM range.
SELECT set_chunk_time_interval('metrics', INTERVAL '1 day');SELECT chunk_name, range_start, range_end, is_compressedFROM timescaledb_information.chunksWHERE hypertable_name = 'metrics'ORDER BY range_start DESCLIMIT 10;-- Chunks matching a predicate are excluded from scans by the planner-- automatically; verify with EXPLAIN that only relevant chunks are touchedEXPLAIN (ANALYZE, BUFFERS)SELECT avg(cpu_pct) FROM metricsWHERE time > now() - INTERVAL '2 hours';
Real-Time Continuous Aggregates
Control whether queries against a continuous aggregate blend in not-yet-materialized recent data.
-- Default: materialized_only = false, so queries transparently union-- finalized buckets with a live query over the raw hypertable for-- the not-yet-refreshed tailALTER MATERIALIZED VIEW metrics_hourly SET (timescaledb.materialized_only = false);-- Disable real-time blending for large dashboards where a few minutes-- of staleness is acceptable and you want a guaranteed-fast queryALTER MATERIALIZED VIEW metrics_hourly SET (timescaledb.materialized_only = true);-- Force an immediate manual refresh of a specific windowCALL refresh_continuous_aggregate('metrics_hourly', now() - INTERVAL '3 hours', now());
Inspecting Background Jobs
Every policy (compression, retention, cagg refresh) runs as a scheduled background job you can audit and tune.
SELECT job_id, application_name, schedule_interval, next_start, configFROM timescaledb_information.jobsWHERE hypertable_name = 'metrics';SELECT job_id, succeeded, start_time, finish_time, total_runtimeFROM timescaledb_information.job_historyORDER BY start_time DESCLIMIT 10;-- Change a policy's cadence after the factSELECT alter_job(job_id, schedule_interval => INTERVAL '30 minutes')FROM timescaledb_information.jobsWHERE hypertable_name = 'metrics' AND proc_name = 'policy_compression';
Advanced Operational Concepts
Terms that matter once a hypertable moves from prototype to production scale.
- Chunk exclusion- the planner statically prunes chunks outside a query's time predicate before execution, the core reason time-range queries stay fast
- materialized_only- controls whether a continuous aggregate blends live raw data with materialized buckets on read
- compress_segmentby- pick a low-cardinality, frequently-filtered column (e.g. device_id) to keep compressed batches efficiently scannable
- Space partitioning (add_dimension)- hash-partitioning on a second column parallelizes writes but multiplies chunk count; avoid over-partitioning
- timescaledb_toolkit- separate extension providing hyperfunctions (percentile_agg, candlestick_agg, counter_agg) not in core TimescaleDB
- reorder_chunk()- physically re-clusters an older chunk's rows on disk to match a target index, improving scan locality
Query continuous aggregates instead of raw hypertables for dashboard panels covering more than a few hours — they're incrementally maintained in the background so reads stay fast even as the underlying hypertable grows into billions of rows.