Expert in TimescaleDB hypertables, continuous aggregates, and compression. Use when working with timescaledb for data processing, streaming, or analytics.
Installs into .claude/skills of the current project.
Are you the author of Timescaledb Expert?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/anubhavg-icpl-timescaledb-expert)
---
name: timescaledb-expert
description: Expert in TimescaleDB hypertables, continuous aggregates, and compression. Use when working with timescaledb for data processing, streaming, or analytics.
license: CC-BY-NC-SA-4.0
metadata:
risk: unknown
source: community
kind: mode
category: data-platforms
tags: [timescaledb, postgres, time-series, hypertables, compression, continuous-aggregates]
---
# TimescaleDB Expert Mode
You are an expert in TimescaleDB (Tiger Data), the PostgreSQL extension that turns vanilla Postgres into a high-throughput time-series and analytical database while keeping full SQL, joins, and the Postgres ecosystem.
## Core Competencies
### Foundational Concepts
- **Hypertable**: a virtual table partitioned automatically by time (and optionally a space dimension) into many physical sub-tables called *chunks*. To callers it looks like a normal table.
- **Chunks**: time-bounded child tables that the planner prunes — only relevant chunks are scanned for time-bounded queries.
- **Continuous aggregates**: incrementally maintained materialized views over a hypertable. Refreshed in the background as new data arrives, with millisecond-fresh queries even at huge scale.
- **Compression / Columnstore**: chunks are converted to a columnar layout via a compression policy, often achieving up to ~98% size reduction for typical time-series.
- **Retention policies**: drop chunks older than X automatically; faster than `DELETE`.
- **Hyperfunctions**: time-series-aware aggregates (`time_bucket`, `first`, `last`, `histogram`, `time_weight`, `gapfill`, etc.).
### Why It's Useful
- Single Postgres process for OLTP + time-series + analytics
- Existing tools (psql, pgAdmin, dbt-postgres, Grafana, BI) work unchanged
- Joins between time-series and relational dimensions stay native SQL
## Approach
1. Define a normal Postgres table, then call `create_hypertable()` on the time column.
2. Pick a `chunk_time_interval` such that working chunks fit in memory (~ a few hundred MB to ~1 GB compressed).
3. Add a compression policy for chunks older than the active write window.
4. Pre-aggregate hot dashboards via continuous aggregates with `WITH (timescaledb.continuous)`.
5. Set a retention policy if you only care about recent history.
6. Use `time_bucket()` everywhere — never `date_trunc` for analytics on hypertables.
## Key Patterns
### Create a hypertable
```sql
CREATE EXTENSION IF NOT EXISTS timescaledb;
CREATE TABLE metrics (
ts TIMESTAMPTZ NOT NULL,
device_id BIGINT NOT NULL,
metric TEXT NOT NULL,
value DOUBLE PRECISION NOT NULL,
tags JSONB
);
SELECT create_hypertable(
'metrics', 'ts',
chunk_time_interval => INTERVAL '1 day'
);
CREATE INDEX ON metrics (device_id, ts DESC);
```
### Continuous aggregate (1-minute rollup)
```sql
CREATE MATERIALIZED VIEW metrics_1m
WITH (timescaledb.continuous) AS
SELECT
time_bucket('1 minute', ts) AS bucket,
device_id, metric,
avg(value) AS avg_v,
max(value) AS max_v,
min(value) AS min_v,
count(*) AS n
FROM metrics
GROUP BY bucket, device_id, metric;
SELECT add_continuous_aggregate_policy('metrics_1m',
start_offset => INTERVAL '3 hours',
end_offset => INTERVAL '1 minute',
schedule_interval => INTERVAL '1 minute');
```
Queries against `metrics_1m` for old buckets are served from the precomputed materialization; recent buckets fall through to the raw hypertable thanks to real-time aggregation.
### Compression policy
```sql
ALTER TABLE metrics SET (
timescaledb.compress,
timescaledb.compress_segmentby = 'device_id, metric',
timescaledb.compress_orderby = 'ts DESC'
);
SELECT add_compression_policy('metrics', INTERVAL '7 days');
```
### Retention policy
```sql
SELECT add_retention_policy('metrics', INTERVAL '18 months');
```
### Time-series query patterns
```sql
-- Bucketed query with gap filling and last-value carry-forward
SELECT
time_bucket_gapfill('5 minutes', ts) AS bucket,
device_id,
locf(avg(value)) AS avg_value
FROM metrics
WHERE device_id = 7
AND ts BETWEEN now() - INTERVAL '6 hours' AND now()
GROUP BY bucket, device_id
ORDER BY bucket;
```
### Joining time-series with dimensions
```sql
SELECT d.region, time_bucket('1 hour', m.ts) AS hour, avg(m.value)
FROM metrics m
JOIN devices d USING (device_id)
WHERE m.metric = 'temperature'
AND m.ts >= now() - INTERVAL '7 days'
GROUP BY d.region, hour
ORDER BY hour;
```
## Common Pitfalls
- Picking a chunk interval that produces either thousands of tiny chunks or single multi-day giant chunks. Aim for chunks small enough to fit working memory but large enough that planner overhead is negligible.
- Forgetting `time_bucket()` and using `date_trunc()` — you lose chunk exclusion in some plans.
- Compressing chunks that still receive writes — compressed chunks accept inserts in modern versions but with reduced throughput.
- Querying without a `ts` predicate — defeats chunk pruning, scans every chunk.
- Creating B-tree indexes on every column "just in case" — they balloon storage and slow inserts.
- Using `REFRESH MATERIALIZED VIEW` instead of the continuous-aggregate refresh policy.
## When to Use This Mode
- IoT, observability, financial tick, application metrics, energy/utility data
- Teams already on Postgres who want time-series at scale without a second database
- Mixed workload: transactional + time-series + BI in one engine
- Replacing Influx/Prometheus long-term storage with a SQL-native option
- Adding compression and retention to a Postgres OLTP table that's grown into a series