Author: Alexey Milovidov, 2026-06-25.
1. (50 min) What's new in ClickHouse 26.6.
2. (10 min) Q&A.
ClickHouse "10 Year Anniversary" Release.
โ 56 new features ๐
โ 79 performance optimizations ๐๏ธ
โ 366 bug fixes ๐ฆ
Append new Enum values without re-typing all the existing ones:
CREATE TABLE events (x Enum8('a' = 1, 'b' = 2)) ...;
-- Before: ALTER ... MODIFY COLUMN x Enum8('a'=1,'b'=2,'c'=3,'d'=4)
ALTER TABLE events MODIFY COLUMN x ADD ENUM VALUES('c' = 3, 'd' = 4);
-- x is now Enum8('a' = 1, 'b' = 2, 'c' = 3, 'd' = 4)
— A pure metadata change — instant, no data rewrite.
— Forgetting an existing value is no longer possible.
Developer: Ilya Golshtein.
Precision arguments and decimal-point control for text output:
SELECT formatReadableSize(1234567890, 4);
-- 1.1498 GiB (default precision is 2: 1.15 GiB)
SELECT 1/3 SETTINGS output_format_float_precision = 4;
-- 0.3333
SELECT 5.0 SETTINGS output_format_always_write_decimal_point… = 1;
-- 5. (keep the decimal point even for whole numbers)
— formatReadableSize / formatReadableQuantity / formatReadableDecimalSize take an optional precision.
Developers: Antonio Filipovic, phulv94, Ilya Yatsishin.
A refreshable MV can now trigger off another RMV's refresh:
CREATE MATERIALIZED VIEW base
REFRESH EVERY 1 HOUR ENGINE = MergeTree ORDER BY day AS …;
CREATE MATERIALIZED VIEW rollup
REFRESH DEPENDS ON base ENGINE = … AS
SELECT day, sum(x) FROM base GROUP BY day;
Developer: Michael Kolupaev.
Two new MergeTree settings decouple projection building from INSERT:
CREATE TABLE ...
SETTINGS materialize_projections_on_insert = 0;
-- INSERTs skip building projection parts โ faster ingestion
CREATE TABLE ...
SETTINGS materialize_projections_on_merge = 1;
-- a merge rebuilds a projection missing from all its source parts
— Tables with many projections ingest faster;
projections are built during merges instead.
Developer: Christoph Wurm.
Key a quota by a subnet, not just a single address:
CREATE QUOTA q KEYED BY ip_address IPV4_PREFIX_BITS 24
FOR INTERVAL 1 hour MAX queries = 1000;
-- every client in a /24 shares one quota bucket
CREATE QUOTA q6 KEYED BY ip_address IPV6_PREFIX_BITS 64 …;
— Rate-limit a whole network range at once.
— Works with IP_ADDRESS and FORWARDED_IP_ADDRESS keys.
— Especially useful for IPv6.
Developer: Aditya Chopra.
Type help <name> in clickhouse-client / clickhouse-local to read the docs
right in the terminal — backed by system.documentation:
:) help MergeTree
— Also /help, man, /man. Resolves ambiguity and typos.
— Markdown rendered in the terminal, with syntax-highlighted SQL.
Demo
Developer: Alexey Milovidov.
System tables that describe the system now describe themselves — with
description, syntax, examples, introduced_in and related columns:
SELECT name, introduced_in
FROM system.table_engines WHERE name ILIKE '%kafka%';
SELECT name, description FROM system.data_type_families;
Now self-documenting: table_engines, database_engines, data_type_families, formats, aggregate_function_combinators, dictionary_layouts, dictionary_sources, data_skipping_index_types, disk_types.
Developer: Alexey Milovidov.
The whole reference manual, as a table, inside the server:
SELECT type, count() FROM system.documentation GROUP BY type;
| type | count |
|---|---|
| Function | 1592 |
| Setting | 1548 |
| Server Setting / MergeTree Setting | 412 / 316 |
| Aggregate Function, Data Type, Format, … | … |
— The same docs as the website — queryable, greppable, offline.
— Good for integration with tools.
Developer: Alexey Milovidov.
More of ClickHouse, introspectable as tables:
SELECT table, name, type, expression FROM system.constraints;
— system.constraints — every table's CHECK / ASSUME constraints.
— system.iceberg_files — introspection of files in Iceberg tables.
Keeper related tables (available on clickhouse-keeper):
— system.keeper_snapshots
— system.keeper_changelogs
— system.keeper_cluster.
Developers: Pedro Ferreira, Miัhael Stetsyuk, Han Fei.
ClickHouse now detects the AI coding agent that launched the client,
from its environment, and records it in a new client_agent column:
SELECT query_id, client_agent, interface FROM system.processes;
| query_id | client_agent | interface |
|---|---|---|
| ai_demo | claude-code | 1 |
— Recognizes Claude Code, Cursor, Codex, Gemini CLI, Goose, …
— Also in system.query_log and system.query_thread_log.
Developer: Alexey Milovidov.
clickhouse-local can now start listening for connections on the fly:
clickhouse-local
:) SYSTEM START LISTEN TCP;
:) SYSTEM START LISTEN HTTP;
-- now other clients can connect to your local session
— Turn an ad-hoc analysis session into a shareable endpoint.
— SYSTEM STOP LISTEN to close it again.
Developer: Alexey Milovidov.
A new built-in web UI that draws the dependency graph of your database:
http://localhost:8123/schema
— Tables, materialized views, refreshable MVs, dictionaries,
distributed tables and views — and the arrows between them.
— See at a glance how data flows from ingestion to rollups.
— Embedded in the server.
Developer: Nikita Mikhaylov.
Render query results as an image. One row per pixel (r, g, b or v),
with implicit or explicitly defined pixel coordinates (x, y):
WITH number DIV 1024 AS y, number MOD 1024 AS x,
L2Norm((x - 512, y - 512)) / 512 AS radius, 60 AS stripe_size,
round((atan2(x - 512, y - 512) / pi() * 180 + exp(radius) * 90)
/ stripe_size) * stripe_size AS alpha,
radius <= 1 ? abs(1 - (radius - 0.5) * (radius - 0.5) * 4) : 0 AS a,
colorOKLCHToSRGB((0.7, 0.15, alpha)) AS rgb
SELECT rgb.1::UInt8 AS r, rgb.2::UInt8 AS g, rgb.3::UInt8 AS b, a
FROM numbers(1048576) FORMAT PNG
SETTINGS output_format_image_terminal_mode = 'kitty'
— The Web UI shows the image directly.
— Great for heatmaps, sparklines, generated art.
Developer: Maksim Dergousov.
date_part('unit', expr)
— PostgreSQL-style sugar for EXTRACT(unit FROM expr):
SELECT date_part('year', toDateTime('2026-06-25 10:30:00')); -- 2026
SELECT date_part('doy', toDate('2026-06-25')); -- 176 (day of year)
SELECT date_part('dow', toDate('2026-06-25')); -- 4 (day of week)
— All the PostgreSQL extras: epoch, dow, doy, isodow, isoyear, century, decade, millennium.
— Also: EXTRACT(TIMEZONE_HOUR / TIMEZONE_MINUTE FROM dt)
and EXTRACT(… FROM INTERVAL n …).
Developers: Alexey Milovidov, Vinayak Joshi.
More SQL-standard / PostgreSQL spellings:
SELECT LOCALTIMESTAMP; -- now() โ DateTime
SELECT LOCALTIME; -- current time of day โ Time
SELECT SESSION_USER; -- alias of currentUser()
— No parentheses needed — they are reserved-word constants.
— Makes more ported queries and BI-tool SQL just work.
Developers: Thomas Cabral, Takumi Hara.
min_by / max_by — aliases for argMin, argMax for PostgreSQL, BigQuery:
SELECT min_by(user, score), max_by(user, score) FROM t;
-- aliases of argMin / argMax
REGEXP_SUBSTR — alias of regexpExtract, for Oracle/MySQL/Snowflake:
SELECT REGEXP_SUBSTR('order 12345 shipped', '[0-9]+');
-- 12345
"These aliases are stupid, real engineers use the original ClickHouse names." — the changelog
ALTER TABLE MODIFY COLUMN x UInt32 NULL; -- same as Nullable(UInt32)
Developers: Joey Yu, Alexey Milovidov, Takumi Hara.
PostgreSQL array quantifiers with a literal array on the right:
SELECT 3 = SOME([1, 2, 3]); -- 1 โ rewritten to has(...)
SELECT 5 > ALL([1, 2, 3]); -- 1 โ arrayAll lambda
SELECT 2 <> ALL([1, 3, 5]); -- 1
The subquery form of ANY / SOME / ALL still lowers to IN / NOT IN.
Developer: Alexey Milovidov.
Pick columns with * LIKE and * ILIKE instead of listing them:
SELECT * ILIKE '%user%' FROM events;
-- user_id, user_name, ... (case-insensitive)
SELECT t.* ILIKE '%id', count() FROM t GROUP BY ALL;
-- qualified form works too
Similar to * MATCH 'regexp' and COLUMNS('regexp'),
but with SQL-style LIKE patterns instead of a regular expression.
Developer: Yue Ni.
Define a custom escape character so % and _ can be literal:
SELECT '50% off' LIKE '50!% off' ESCAPE '!';
-- 1 (the !% matches a literal percent sign)
SELECT 'a_b' LIKE 'a!_b' ESCAPE '!';
-- 1 (the !_ matches a literal underscore)
— SQL-standard syntax, also accepted by PostgreSQL, MySQL, …
Developer: Ilya Yatsishin.
Read a GeoJSON FeatureCollection — one row per feature:
SELECT id, properties.name, toTypeName(geometry)
FROM file('places.geojson', GeoJSON);
| id | properties.name | geometry |
|---|---|---|
| 1 | London | Geometry (Point) |
| 2 | square | Geometry (Polygon) |
— Columns: id String, geometry Geometry, properties Nullable(JSON).
— Point, LineString, MultiLineString, Polygon, MultiPolygon supported natively.
Developer: Mark Needham.
Serve map tiles straight out of ClickHouse with the new MVT functions:
-- Project lon/lat into a tile's pixel space:
SELECT MVTEncodeGeom((13.37, 52.52)::Point, 10, 550, 335);
-- (124, 3384)
-- Aggregate a group's geometries into one binary tile:
SELECT MVTEncode(geom_in_tile_space) FROM ...;
-- Tile bounding box for the WHERE clause:
SELECT MVTBoundingBox(12, 1205, 2557);
— Point, line and polygon geometry.
— Also available as PostGIS aliases ST_AsMVTGeom and ST_AsMVT.
Developer: Saarthak Gupta.
A round of Keeper improvements makes it more twice as fast overall:
— Better request batching.
— Pipelining messages to the leader.
— Pipelining log appends.
— Snapshot I/O moved to a background thread (nuraft_use_bg_thread_for_snapshot_io).
— Lower peak memory when applying received snapshots.
On a realistic workload with keeper-bench:
26.5: 2899 RPS, 41.4 ms latency
26.6: 7230 RPS, 18.3 ms latency
Developers: Michael Kolupaev, Antonio Andelic, Mikhail Filimonov.
Per-query overhead for simple queries is significantly reduced:
SELECT count() FROM hits;
-- parsing + analysis + planning ~50% faster from a single connection
More analysis wins, all enabled by default:
— Identifier-resolution caching during analysis.
— Fast analysis of deeply nested subqueries.
— Faster resolution for deeply nested function calls.
— Faster processing of changing access control entities.
Deeply nested SELECT * over a 1200-column table:
analysis 1.46 s → 0.023 s (~64x), 26.5 vs 26.6.
Developers: Raรบl Marรญn, Dmitry Novik, Max Justus Spransy, Azat Khuzhin.
Long or high-cardinality primary keys used to make index analysis slow.
Now (use_lightweight_primary_key_index_analysis, on by default):
— For a long primary key, analysis time depends on the columns the query
actually filters on — not on the length of the key.
Extending the sorting key is now nearly free for index analysis.
— For a high-cardinality key, analysis works on just the selective
in-memory prefix instead of loading the whole key.
Developer: Nihal Z. Miaji.
The general-purpose comparison sorts are now C++ ports
of ipnsort and driftsort:
— Faster ORDER BY on non-numeric columns and for stable sorts.
— Removes a worst case on already reverse-sorted input.
— State-of-the-art, adaptive, and battle-tested algorithms.
ORDER BY 20M strings, single thread:
7.1 s → 5.9 s (~1.2x), 26.5 vs 26.6.
Developer: Alexey Milovidov.
— Sharded aggregation for high-cardinality, evenly-distributed keys
(enable_sharding_aggregator = 1): hash the key to scatter rows across
threads, so each thread aggregates a disjoint subset — no merge phase.
— Hash-table prefetching for string-key GROUP BY: ~8% faster.
— Lower peak memory when merging large two-level aggregation states
(e.g. groupArray).
— More optimal handling of Nullable columns in aggregation.
— Packed keys32 / keys64 methods in HashJoin and Set.
Measured — 300M-row high-cardinality GROUP BY, 16 threads:
enable_sharding_aggregator: 2.31 s → 1.87 s.
Developers: Nihal Z. Miaji, Le Zhang, Yuri Fedoseev, Nikita Taranov.
A whole family of new optimizations for LIMIT BY (all on by default):
— Run LIMIT BY inside each parallel sorted stream during ORDER BY
when its keys are a prefix of the sort key.
— Stream LIMIT N BY cols in primary-key order with O(1) memory per stream.
— Apply LIMIT BY per partition in parallel when the partition is a
function of the keys.
— Drop redundant / injective key expressions
(LIMIT 5 BY toString(x) → LIMIT 5 BY x).
Measured — LIMIT 3 BY g over a 50M-row MergeTree (g is in the sort key):
memory 86 MiB → 22 MiB (~4x), time 0.15 s → 0.09 s.
Developer: Nihal Z. Miaji.
A batch of JOIN planner and runtime improvements, mostly on by default:
— enable_join_transitive_predicates on by default — push derived
predicates across the join (filter both sides for free).
— Build-side FixedHashMap doubles as the probe-side runtime filter.
— Lazy selector / replication indexes when a JOIN is followed by a
selective LIMIT / TopN / another JOIN.
— parallel_hash is now allowed for ASOF JOIN.
— Packed keys32 / keys64 in HashJoin; fast path for single-row probes.
— Dynamic Programming join reordering now works with parallel replicas.
Developers: A. Gololobov, N. Taranov, H. Selmi, Xiaozhe Yu, G. Maher.
A SELECT that never ends — the first step toward streaming queries. Append STREAM and it keeps emitting new rows as they are inserted:
SET enable_streaming_queries = 1;
SELECT id, msg FROM live_events STREAM; -- blocks, keeps streaming
Applications: filtering and real-time alerting on a stream of events.
Advanced usage with cursors:
SELECT _block_number AS bn, _block_offset AS bo, id, msg
FROM events STREAM CURSOR {'all': {'block_number': 2, 'block_offset': 0}}
Developer: Mikhail Artemenko.
The experimental distributed planner splits a query plan into stages
connected by exchanges — and ships fragments to worker nodes:
— scatter / broadcast / gather / shuffle exchanges.
— Distributed shuffle and broadcast hash joins, shuffle aggregation, distributed sort.
— Data between stages streamed over TCP or temp files in object storage.
— Several workers per host (stateless_worker_port / streaming_exchange_port).
Controlled by a new setting, make_distributed_plan.
Developer: Alexander Gololobov.
The workload scheduler — which already manages CPU, I/O and concurrency — now manages memory too:
CREATE RESOURCE memory (MEMORY RESERVATION);
CREATE WORKLOAD all;
CREATE WORKLOAD prod IN all SETTINGS max_memory = '100G';
CREATE WORKLOAD reports IN all SETTINGS max_memory = '20G';
CREATE WORKLOAD vasya IN reports SETTINGS weight = 1;
CREATE WORKLOAD petya IN reports SETTINGS weight = 2;
-- route a query to a workload:
SELECT … SETTINGS workload = 'prod';
-- reserve memory before processing:
SET reserve_memory = '5G';
Developer: Sergei Trifonov.
Ask "what if I had this skip index?" — without building it:
CREATE HYPOTHETICAL INDEX idx_level ON logs (level) TYPE set(2) GRANULARITY 4;
EXPLAIN WHATIF SELECT count() FROM logs WHERE level = 'error';
Baseline (PK + partition + existing indexes): marks: 612, 19.56 MiB
With idx_level (set, hypothetical):
status: applicable
marks: 200 (was 612)
est_bytes: 6.39 MiB
skip_ratio: 67.3%
Estimation: empirical, sampled 5/5 parts
— Session-scoped, visible in system.hypothetical_indexes.
Tune indexes data-driven.
Developer: Yarik Briukhovetskyi.
Generate text embeddings from inside ClickHouse, via an LLM API:
SELECT aiEmbed('qwen3-8b', review_text) AS embedding
FROM reviews
LIMIT 10;
-- Array(Float32) per row, ready for cosineDistance / vector search
— Build a semantic-search or RAG pipeline without leaving SQL.
— Pairs with the new quantization functions for compact vector indexes.
— Experimental — configure your embedding provider and model.
Developer: George Larionov.
A scalar codec that compresses embedding components to 8bit and below:
SELECT quantizeBFloat16ToInt8(1.5::BFloat16);
-- 107
SELECT dequantizeInt8ToBFloat16(quantizeBFloat16ToInt8(1.5::BFloat16));
-- 1.5 (near-lossless round-trip)
— 256-level Gaussian Lloyd–Max quantizer; one byte per component.
— Int4 / Int2 / binary codes fall out by bit-truncation — trade size for recall.
— Shrinks vector indexes 4x (or more) for cheaper similarity search.
Developer: Alexey Milovidov.
— ๐ฆ๐บ Sydney: Agentic AI Unplugged, Jun 24
— ๐บ๐ธ Seattle Iceberg Meetup, Jun 25
— ๐ฒ๐พ Kuala Lumpur Meetup, Jun 26
— ๐ฐ๐ท Seoul: ClickHouse + Confluent, Jun 30
— ๐บ๐ธ San Francisco: AI Demo Night, Jul 1
— ๐ฎ๐ณ Mumbai: Lakes to Queries, Jul 4
— ๐บ๐ธ New York: AI Builders Night, Jul 8
— ๐ฉ๐ช Berlin: WeAreDevelopers, Jul 8
— ๐จ๐ฆ Montreal: OSS Happy Hour, Jul 9
— ๐ฎ๐ณ Bangalore: Fast Data at Scale, Jul 11
— ๐ฆ๐บ Melbourne Meetup, Jul 16
— ๐ฏ๐ต Tokyo: Google Cloud Next, Jul 30
— Ten years of open source
— How ClickHouse became fast at joins
— Integrating the Rust Delta Kernel into ClickHouse
— Introducing multi-stage distributed query execution
— CostBench: an open benchmark for data warehouse cost-performance
— TPC-H for less than a cent: ClickHouse Cloud
vs. Snowflake, Databricks, BigQuery, and Redshift
— The end-to-end cost-performance: Snowflake vs. ClickHouse Cloud
— The future of observability: thousands of agents, not one proprietary AI