TigerData logo
TigerData logo
  • Product

    Product

    Tiger Cloud

    Robust elastic cloud platform for startups and enterprises

    TimescaleDB Enterprise

    Self-managed TimescaleDB for on-prem, edge and private cloud

    Open source

    TimescaleDB

    Time-series, real-time analytics and events on Postgres

    Search

    Vector and keyword search on Postgres

  • Industry

    Data Centers

    Energy & Utilities

    Oil & Gas Operations

    Smart Manufacturing

    Crypto

  • Docs
  • Pricing
  • Developer Hub

    Changelog

    Benchmarks

    Blog

    Community

    Customer Stories

    Events

    Support

    Integrations

    Launch Hub

  • Company

    About

    TigerData logo

    Timescale

    Partners

    Security

    Careers

Contact usStart a free trial
Tiger Data

Products

  • TimescaleDB
  • Tiger Cloud
  • TimescaleDB Enterprise
  • Postgres Search Stack

Industry

  • Data Centers
  • Energy & Utilities
  • Oil & Gas Operations
  • Smart Manufacturing
  • Crypto

Support

  • Cloud Status
  • Support
  • Security
  • Terms of Service
  • Code Of Conduct

Learn

  • Documentation
  • Blog
  • Tutorials
  • Changelog
  • Success Stories

Company

  • About
  • Contact Us
  • Careers
  • Newsroom
  • Brand
  • Events

Products

  • TimescaleDB
  • Tiger Cloud
  • TimescaleDB Enterprise
  • Postgres Search Stack

Industry

  • Data Centers
  • Energy & Utilities
  • Oil & Gas Operations
  • Smart Manufacturing
  • Crypto

Support

  • Cloud Status
  • Support
  • Security
  • Terms of Service
  • Code Of Conduct

Learn

  • Documentation
  • Blog
  • Tutorials
  • Changelog
  • Success Stories

Company

  • About
  • Contact Us
  • Careers
  • Newsroom
  • Brand
  • Events
Privacy preferencesLegalPrivacySitemap

Subscribe to the Tiger Data newsletter

Gold Partner with Inductive Automation — Ignition

2026 (c) Timescale, Inc., d/b/a Tiger Data.
All rights reserved.

Tiger Data
GOLD PARTNER WITHINDUCTIVE AUTOMATION

2026 (c) Timescale, Inc., d/b/a Tiger Data.
All rights reserved.

Privacy preferencesLegalPrivacySitemap

TimescaleDB 2.28: Faster Queries, Lighter Operations, and Better Schema Evolution

N

By Nicole Ghalwash

July 29th, 2026

6 min

Share

N

By Nicole Ghalwash

July 29th, 2026

6 min

Share

Copy as HTML

Open in ChatGPT

Open in Claude

Open in v0

Announcements & Releases

TimescaleDB

Table of contents

  1. 01 TL;DR
  2. 02 Schema evolution: ADD COLUMN on CAggs
  3. 03 How we made continuous aggregates operational at scale
  4. 04 Faster queries on compressed data
  5. 05 Better operational flexibility and efficiency
  6. 06 PostgreSQL 15: Final Release and Migration Path
  7. 07 Upgrade to 2.28 today
Get started for free
TimescaleDB 2.28: Faster Queries, Lighter Operations, and Better Schema Evolution

Time-series analytics at scale creates operational friction. When you're running continuous aggregates, columnar storage, and complex analytical patterns, each new metric, query pattern, and configuration tuning attempt adds complexity. Refreshes block each other, configuration changes require rebuilds, and new aggregates mean recomputing entire rollups.

Over recent TimescaleDB releases, we've prioritized minimizing these hurdles by leveraging bloom filters to bypass redundant processing during high-volume operations on columnar storage. We also expanded vectorized execution across more query patterns and simplified continuous aggregate workflows by combining refresh and compression.

Now with TimescaleDB 2.28, we're making common analytical queries faster without code changes, making continuous aggregate operations less disruptive and more flexible, and eliminating friction when evolving your schema and configuration. The result is faster queries, lighter operations, and the ability to evolve your analytics alongside your application as it scales.

TL;DR


Lighter, more flexible continuous aggregates:

  • ADD COLUMN on CAggs: Add new aggregations in place without rebuilding
  • Fine-grained locking: Refreshes no longer serialize unrelated operations
  • Incremental refresh batching: Break large refreshes into batches instead of one heavy operation
  • ANALYZE and VACUUM: Maintenance commands now work directly on continuous aggregates

Faster queries on compressed data:

  • Metadata-driven first() and last() queries: Answer from batch metadata without decompression
  • Vectorized CASE expressions: Conditional logic stays on the fast path
  • Batch Sorted Merge for more queries: Lighter-weight sorting on compressed data

Better operational flexibility:

  • Sparse index retrofitting: Update configuration on existing chunks without recompression
  • Compression settings clarity: Warnings prevent misconfiguration
  • GUC for bulk loads: Skip invalidation tracking during migrations
  • Nullable ORDER BY safety: Fallback to correct compression path

Also: PostgreSQL 15 support ends with 2.28. Plan your upgrade to PG16, PG17, or PG18.

Schema evolution: ADD COLUMN on CAggs

Schema evolution lets you add new aggregated columns without dropping and rebuilding. Before 2.28, adding a metric meant recreating the entire CAgg, recomputing all historical data (hours on large datasets), and breaking downstream consumers. Now you can ADD COLUMN with GENERATED ALWAYS AS and backfill incrementally.

-- Add a new metric to an existing CAgg ALTER MATERIALIZED VIEW conditions_summary_hourly ADD COLUMN max_temp double precision GENERATED ALWAYS AS ( max(temperature) ) STORED; -- Backfill historical data CALL refresh_continuous_aggregate( 'conditions_summary_hourly', NULL, NULL, force => true );

How we made continuous aggregates operational at scale

Real-time dashboards on operational data used to force a choice: refresh frequently and block bulk loads, or refresh less often and accept stale data. Every continuous aggregate refresh acquired a table-wide lock on its materialized hypertable, blocking concurrent operations. In 2.28, we switched to row-level locking on the continuous aggregate catalog entry. Only one refresh processes a CAgg's invalidation log at a time, but the materialized table is free for concurrent operations. Refreshes and bulk loads now run in parallel.

We also made continuous aggregates more flexible. ADD COLUMN on CAggs enables you to evolve your analytical schema in production by adding new aggregations as you learn what data you need, without downtime or rebuilds. Your dashboard design no longer locks you into upfront predictions. 

image
-- Before 2.28: refresh acquired table-wide lock -- After 2.28: refresh acquires only catalog row lock CALL refresh_continuous_aggregate( 'metrics_hourly', '2026-01-01', '2026-02-01' ); -- Other refreshes, bulk loads, and DDL can proceed

2.28 also adds two capabilities that make continuous aggregates evolve with your application:

  1. Incremental refresh batching processes large time windows in smaller batches instead of single long-running transactions. Before 2.28, refresh_continuous_aggregate() materialized the entire window atomically, holding resources and blocking vacuums. Large windows could take hours and fail mid-way. Now you can batch them.
-- Refresh a 30-day window in smaller batches CALL refresh_continuous_aggregate( 'metrics_hourly', now() - INTERVAL '30 days', now(), options => '{ "buckets_per_batch": 10, "max_batches_per_execution": 20 }'::jsonb );
  1. Schema evolution lets you add new aggregated columns without dropping and rebuilding. Before 2.28, adding a metric meant recreating the entire CAgg, recomputing all historical data (hours on large datasets), and breaking downstream consumers. Now you can ADD COLUMN with GENERATED ALWAYS AS and backfill incrementally.
-- Add a new metric to an existing CAgg ALTER MATERIALIZED VIEW conditions_summary_hourly ADD COLUMN max_temp double precision GENERATED ALWAYS AS ( max(temperature) ) STORED; -- Backfill historical data CALL refresh_continuous_aggregate( 'conditions_summary_hourly', NULL, NULL, force => true );

ANALYZE and VACUUM now work on continuous aggregates directly. Maintenance commands automatically maintain accurate planner statistics across the materialized hypertable and all its chunks, ensuring query plans stay optimal.

Faster queries on compressed data

One of the most common queries in time-series workloads is "give me the latest value per series." Dashboards and monitoring systems run it constantly. It's a simple pattern: find the first or last value in a time range. But on compressed data, answering that query used to require decompressing entire batches just to find values that are already in the metadata. Time-sorted batches store first and last values as metadata. But before 2.28, the database decompressed anyway to answer those queries.

In 2.28, TimescaleDB extracts first(value, time) and last(value, time) aggregates directly from batch sparse indexes. No decompression. For "latest reading" queries that consume significant resources at scale, that means meaningful speedup with zero query changes. Here's what that looks like:

image
-- Define firstlast sparse index ALTER TABLE metrics SET ( tsdb.index = 'firstlast(temperature)' ); -- Note: Use rebuild_sparse_index() to retrofit existing chunks -- New chunks now answer this query from metadata alone SELECT device_id, last(temperature, time) FROM metrics WHERE time > now() - INTERVAL '7 days' GROUP BY device_id;

Beyond first/last, we also extended vectorized execution to cover CASE expressions. Before 2.28, conditional logic in aggregations forced columnar queries to fall back to row-by-row processing, making identical queries perform very differently depending on whether a CASE expression was present.

-- This query now stays fully vectorized SELECT time_bucket('1 hour', ts) AS bucket, SUM( CASE WHEN status_code >= 500 THEN 1 ELSE 0 END ) AS error_count, AVG( CASE WHEN latency_ms > 1000 THEN latency_ms END ) AS slow_avg_latency FROM requests WHERE ts > now() - INTERVAL '7 days' GROUP BY bucket;

Pivot-style queries, error rate calculations, and conditional metrics all stay on the fast vectorized path. For workloads that use conditional aggregations on compressed history, that eliminates the surprise performance cliffs.

Finally, Batch Sorted Merge now applies to more query patterns. ORDER BY queries on unordered compressed chunks without segmentation no longer fall back to expensive external sorts. The planner now replaces those sorts with lightweight metadata merges over pre-sorted batches.

Better operational flexibility and efficiency

Tuning time-series workloads means iterating on compression strategy. But historically, every configuration change (adding a sparse index, adjusting segmentby, optimizing orderby) only applied to future compressions. Existing chunks kept their old settings, creating inconsistent performance and confusion about why newly tuned queries behaved differently.

2.28 eliminates that friction. rebuild_sparse_index lets you retrofit sparse index configuration to existing chunks without recompressing. Sparse indexes are metadata. Updating them shouldn't require rewriting data. Below is the actual code snippet you can leverage:

-- Change sparse index settings on the hypertable ALTER TABLE metrics SET ( tsdb.index = 'minmax(value)' ); -- Retrofit existing chunks without recompression SELECT _timescaledb_functions.rebuild_sparse_index( '_timescaledb_internal._hyper_1_42_chunk' );

Compression settings changes now emit warnings clarifying that new settings apply only to future compressions. This closes the gap: users assume ALTER TABLE sets configuration across the entire dataset, but it only affects new chunks.

For correctness, nullable ORDER BY columns now safely fall back to decompress-compress during recompression, preventing silent incorrect results when min/max metadata doesn't account for NULLs.

Gapfill row count estimation improved so time_bucket_gapfill queries get more accurate planner statistics and better query plans.

For bulk migrations, a new GUC timescaledb.skip_cagg_invalidation suppresses continuous aggregate invalidation tracking during bulk loads. Migration tools no longer generate useless invalidation entries that trigger expensive refresh storms.

PostgreSQL 15: Final Release and Migration Path

PostgreSQL 15 support ends with TimescaleDB 2.28. Going forward, only PostgreSQL 16, 17, and 18 are supported.

If you're on PG15, plan your upgrade to PG17 or PG18 now. We'll begin upgrading instances in production the week of September 15th, so you should plan your migration path over the next two months to avoid downtime. Postgres upgrades are typically non-disruptive (seconds to minutes of downtime using logical replication or physical backup/restore), and newer Postgres versions bring improvements in query parallelism, vector search optimization, and compression.

Upgrade to 2.28 today

For workloads running continuous aggregates at scale, columnar queries with conditional logic, or iterating on compression configuration, 2.28 removes operational friction: refreshes don't serialize, queries stay vectorized, and tuning doesn't require rewrites.

2.28 is available now. To learn more, check out the full release notes for a complete list of improvements, or try Tiger Cloud for free and experience TimescaleDB 2.28 on your largest hypertables. We welcome your feedback on GitHub. Please note that for Tiger Cloud customers, all improvements are live immediately. For self-hosted deployments, download the latest release and follow the upgrade guide.

// Related posts

TimescaleDB 2.27: Broader Vectorized Execution, Up to 160x More Efficient UPDATE/DELETE, and Smarter UPSERT Pruning
TimescaleDB 2.27: Broader Vectorized Execution, Up to 160x More Efficient UPDATE/DELETE, and Smarter UPSERT Pruning

Announcements & Releases

TimescaleDB

TimescaleDB 2.27: Broader Vectorized Execution, Up to 160x More Efficient UPDATE/DELETE, and Smarter UPSERT Pruning

Up to 160x more efficient UPDATE/DELETE, 30% to 2x faster vectorized execution, and skip unnecessary decompression + compression. Here's what's new in TimescaleDB 2.27.

By Brandon Purcell

June 9th, 2026

TimescaleDB 2.26: 3.5x Faster time_bucket() Aggregations, 70x Faster Summary Queries, and Faster Multi-Column Lookups
TimescaleDB 2.26: 3.5x Faster time_bucket() Aggregations, 70x Faster Summary Queries, and Faster Multi-Column Lookups

Announcements & Releases

TimescaleDB

TimescaleDB 2.26: 3.5x Faster time_bucket() Aggregations, 70x Faster Summary Queries, and Faster Multi-Column Lookups

TimescaleDB 2.26 delivers 3.5x faster time_bucket() aggregations, 70x faster summary queries, and 2x faster multi-column lookups. No query rewrites needed.

By Brandon Purcell

April 22nd, 2026

pg_textsearch 1.0: How We Built a BM25 Search Engine on Postgres Pages
pg_textsearch 1.0: How We Built a BM25 Search Engine on Postgres Pages

Announcements & Releases

pg_textsearch

pg_textsearch 1.0: How We Built a BM25 Search Engine on Postgres Pages

pg_textsearch 1.0 brings native BM25 search to Postgres. No Elasticsearch sidecar needed. Learn how it works and see benchmarks vs. ParadeDB at 138M documents.

By Todd J. Green

March 31st, 2026

Stay updated with new
posts and releases.

Receive the latest technical articles and release notes in your inbox.