Author: Alexey Milovidov, 2026-09-24.
1. (50 min) What's new in ClickHouse 26.9.
2. (10 min) Q&A.
— 56 new features ๐
— 135 performance optimizations ๐
— 464 bug fixes ๐ฟ๏ธ
Hard limits for multi-tenant, temporary, and demo services:
CREATE TABLE t (…) ENGINE = MergeTree ORDER BY id
SETTINGS max_table_size_rows = 1000000;
-- also max_table_size_bytes_compressed, ..._uncompressed
INSERT INTO t …
-- Table size limit exceeded: ... 1200000, which exceeds
-- the 'max_table_size_rows' setting value (1000000).
CREATE DATABASE tenant ENGINE = Atomic SETTINGS max_tables = 100;
-- Too many tables in database `tenant`. (TOO_MANY_TABLES)
— Checked at the start of INSERT and when parts are committed.
— Tables, views, and dictionaries count towards max_tables.
Developer: Alexey Milovidov.
A Merge table can now be read with parallel replicas —
and the aggregation above it is distributed too:
CREATE TABLE logs_all ENGINE = Merge(default, '^logs_');
SELECT toYear(d), count() FROM logs_all GROUP BY 1
SETTINGS enable_parallel_replicas = 1, parallel_replicas_plan_based = 1,
parallel_replicas_allow_merge_tables = 1;
EXPLAIN: ReadFromParallelReplicas (Aggregating
Union: ReadFromMergeTree (logs_2025), (logs_2026))
— The Merge is expanded into a union of MergeTree reads
before the plan is distributed — nothing Merge-specific on replicas.
— FINAL and non-MergeTree children fall back to one replica.
Developer: Igor Nikonov.
The SQL-standard family, all nine of them:
SELECT regr_slope(y, x), regr_intercept(y, x),
regr_r2(y, x), regr_count(y, x)
FROM VALUES('x Float64, y Float64',
(1, 3.1), (2, 4.9), (3, 7.2), (4, 8.8), (5, 11.1));
-- 1.99 โ 1.05 โ 0.9973 โ 5 โ y โ 2x + 1
— Also regr_avgx, regr_avgy, regr_sxx, regr_syy, regr_sxy.
— The same names as in PostgreSQL, Oracle, Snowflake
— queries port as is.
— Combinators, GROUP BY, and window functions work as usual.
Developer: mosya415.
Paths of a JSON column can be addressed like map keys:
SELECT json['user']['name'], json['tags'] FROM events;
-- the same as json.user.name and json.tags
SELECT json['user.name'], json['first name'] FROM events;
-- keys with dots and spaces โ without backticks
— Translated by the parser to nested arrayElement calls;
the result is the same Dynamic subcolumn.
— Familiar to everyone coming from Map columns.
Developer: Pavel Kruglov.
How many elements are in a nested array — at all levels?
SELECT arrayFlattenedLength([[1, 2], [3], [4, 5, 6]]),
length([[1, 2], [3], [4, 5, 6]]);
-- 6 โ 3
— length(arrayFlatten(arr)) without materializing the flattened array.
— Matches PostgreSQL's cardinality, which counts every level;
our cardinality is an alias of length and counts the outer array.
Developer: David Meng.
The documentation of every SQL statement — inside the server:
SELECT syntax FROM system.statements WHERE name = 'SELECT';
— Columns: name, syntax, description, parent_name, related.
— The same source that renders the docs - always matching the binary.
— Next to system.functions, system.settings, system.formats...
Demo
Developer: Robert Schulze.
"Which queries did I just run?" — without assigning ids by hand:
SELECT count() FROM numbers(10);
SELECT sum(number) FROM numbers(100);
SELECT query, query_duration_ms FROM system.query_log
WHERE query_id IN (SELECT query_id FROM system.session_query_ids)
AND type = 'QueryFinish' ORDER BY event_time_microseconds;
-- SELECT count() FROM numbers(10) โ 234
-- SELECT sum(number) FROM numbers(100) โ 112
— Query ids of the current session, in execution order.
— For test and benchmark scripts; bounded by
session_query_ids_history_size (1000); TRUNCATE clears it.
Demo
Developer: Vladimir Cherkasov.
A third mode for S3Queue, for a single ingesting server:
CREATE TABLE queue (WatchID UInt64, URL String, EventDate Date)
ENGINE = S3Queue('s3://bucket/landing/*.parquet', NOSIGN, 'Parquet')
SETTINGS mode = 'exclusive';
CREATE MATERIALIZED VIEW mv TO events AS SELECT * FROM queue;
— unordered and ordered track every file in Keeper,
so that many servers can share one queue.
— exclusive tracks files in the memory of the server: no Keeper
round trip per file — for a bucket that belongs to this server alone.
Developer: Ivan Tkatchev.
Column statistics (default since 26.4) store the exact min and max
of every numeric-like column per part. Aggregations read them now:
SELECT min(EventDate), max(EventDate), count() FROM hits;
-- no data is read, answered from statistics
EXPLAIN: Aggregating
โโโ ReadFromPreparedSource (_statistics_min_max_projection)
Example: 26.8: 8–24 ms. 26.9: 2 ms.
— Parts without statistics are read normally and merged in.
— Setting: use_statistics_for_min_max_aggregation.
Developer: Alexey Milovidov.
Wide tables where most columns are empty in most inserts:
stop writing the emptiness.
CREATE TABLE wide (id UInt64, a String, b UInt64, c Array(UInt32), j1 JSON, j2 JSON)
ENGINE = MergeTree ORDER BY id
SETTINGS skip_empty_columns_on_insert = 1,
serialization_info_version = 'with_missing_columns';
INSERT INTO wide (id, a, j1) … -- b, c, j2 are all defaults in this block
-- serialization.json: "missing_columns": [{"name":"b","type":"UInt64"}, ...]
-- files in the part: 49 โ 33; no data streams at all for b, c, j2
— Reads reconstruct the default of the recorded type; the marker
survives merges, mutations, replication, backups.
— Opt-in: keep the old serialization version during a rolling upgrade.
Developer: Amos Bird.
At the end of every query the server sends a burst of tiny messages:
profile info, progress, profile events, end of stream …
Each used to be its own TCP packet:
$ tcpdump -i lo 'tcp src port 9000' # SELECT 1, server -> client
26.8: 23 10 10 3133 14 10 1 - 7 packets
26.9: 23 3278 - 2 packets
— After an idle period Linux shrinks the congestion window;
a burst longer than it waits for an ACK: a full extra round trip.
On a 30 ms link that was ~2x latency for clients that idle between queries.
— Now the end-of-query messages go in one socket write.
Developer: Kaviraj Kanagaraj.
The text index served %word% since 26.7 by scanning its dictionary.
Now prefix and suffix patterns work too:
SELECT count() FROM docs WHERE body LIKE 'clickhouse%'; -- startsWith
SELECT count() FROM docs WHERE body LIKE '%clickhouse'; -- endsWith
EXPLAIN indexes = 1:
Prewhere: endsWith(body, 'clickhouse') AND __text_index_idx_endsWith_โฆ
Skip: Name: idx, Granules: 4/2442
— The index is a hint: posting lists select granules,
the original condition re-checks the rows.
— LIKE … ESCAPE uses text, ngram, token, sparse-gram indexes now.
— The array tokenizer serves arbitrary LIKE/ILIKE patterns.
Developers: Elmi Ahmadov.
Graviton, Ampere, Apple, NVIDIA Grace — a batch of ARM-specific fixes:
— memcpy: the small-copy helper in string, join, and reading paths
compiled into a libc memcpy call on AArch64 — the exact call
it exists to avoid. Fixed on x86 in 2024; now inline on ARM too.
— Filters: a low-level optimization for WHERE using NEON register
accumulation makes it up to 15x faster.
— Also: speed-up of T64 codec, 128-bit division, UTF-8 validation.
Example: DISTINCT over 100M short strings, one thread, Graviton3:
26.8: 12.4 sec. 26.9: 7.6 sec.
Developers: Joshua Dorst, Harikrishnan Prabakaran.
Merging sorted streams is the core of MergeTree: merges, FINAL,
ORDER BY, optimize_on_insert. Three optimizations of k-way merge:
— Multi-column and collated keys: a sorted array of cursors
instead of a heap — 7–8% faster.
— Non-intersecting parts (time-ordered inserts): the whole cursor
is one batch, detected in O(1) comparisons.
— Runs of duplicate keys are skipped: FINAL on ReplacingMergeTree
uses up to 40% less CPU, optimize_on_insert dedup is ~3x faster.
Maksim Kita's blog post with the ideas and explanations.
Developer: Alexey Milovidov.
A condition on an ARRAY JOIN element now drives skip-index analysis,
as arrayJoin(col) IN (…) already did:
CREATE TABLE t (id UInt64, tags Array(String),
INDEX bf tags TYPE bloom_filter) …
SELECT count() FROM t ARRAY JOIN tags AS tag WHERE tag IN ('rare7', 'rare8');
-- 26.8: Granules 1223/1223 โ full scan
-- 26.9: Skip bf โ Granules 2/1223
— arrayJoin is now planned as the ARRAY JOIN operator internally.
— A WHERE on the element is applied before the expansion:
non-matching elements never become rows.
Developers: Yarik Briukhovetskyi.
A JSON column could be hundreds of substreams; on S3 their marks
were loaded one after another - one round trip each:
-- github.com/ClickHouse/datasets, attached from R2 as a plain_rewritable disk:
ATTACH DATABASE datasets UUID 'e053c139-f7b8-4c82-acb7-6b259c5b19e8' ENGINE = Atomic
SETTINGS disk = disk(type = 's3_plain_rewritable',
endpoint = 'https://data.clickhouse.com/public-datasets/db/',
no_sign_request = true, readonly = true),
lazy_load_tables = true;
SELECT data.wiki.:String AS wiki, count() AS edits FROM datasets.wikipedia_edits
WHERE time >= now() - INTERVAL 1 DAY AND data.type.:String = 'edit'
GROUP BY wiki ORDER BY edits DESC LIMIT 5; -- data is a JSON column
Example: 26.8: 8.3 sec, 1733 requests, 235 connections.
26.9: 6.3 sec, 1512 requests, 59 connections.
— Marks of all streams load asynchronously, in parallel by default;
empty streams' marks are not loaded; JSON metadata is read only once.
Developers: Alexey Milovidov, Rory Shanks, Pavel Kruglov.
In previous versions, connections that read to the end of a file,
were held instead of returned to the connection pool.
Unnecessary extra connections and frequent TLS handshakes.
-- one row of the bluesky_car_records JSON column, 4723 substreams:
26.8: 29,714 connections created โ over the hard limit of 25,000:
Cannot create new connection to data.clickhouse.com:443
26.9: 2,885 connections, 265,563 reused
-- wikipedia_edits, edits per wiki (Compact parts): 235 โ 59 connections
— In 26.9, connections are better reused for subsequent requests.
Developer: Alexey Milovidov.
Which join orders are valid with SEMI, ANTI, and OUTER joins?
Every non-inner join used to be a barrier: a selective SEMI join
stayed pinned on top of the inner joins feeding it.
SET query_plan_optimize_join_order_algorithm = 'dpsub',
query_plan_optimize_join_order_use_conflict_detector_c = 1;
EXPLAIN SELECT count() FROM big1 JOIN big2 ON big1.k = big2.k
LEFT SEMI JOIN small ON big1.k = small.k;
-- before: (big1 โ big2) โ small -- 10M โ 10M first, then filter
-- after: big2 โ (big1 โ small) -- the 1000-row semi join goes first
— CD-A and CD-C from the SIGMOD'13 paper "On the Correct and Complete
Enumeration of the Core Search Space" — the state of the art.
Developer: Fisnik Kastrati.
Each step of a recursive CTE writes its result into an intermediate
Memory table, which the next step reads — one block per chunk:
WITH RECURSIVE frontier AS (
SELECT 1 AS node, 0 AS depth
UNION ALL
SELECT e.dst, depth + 1 FROM edges e JOIN frontier f ON e.src = f.node
WHERE depth < 6)
SELECT count() FROM frontier;
— Graph searches built from many granule-sized reads (or ARRAY JOIN
expansions) accumulated thousands of tiny blocks per step.
— Chunks are now squashed before the write, like a regular INSERT.
Developer: Zach Naimon.
Approximate distinct counting per group — the shape of every
"unique users per page" query:
SELECT URL, uniq(UserID) FROM hits GROUP BY URL;
SELECT k, uniqCombined64(v) FROM t GROUP BY k;
— With many interleaved groups every row lands in a different state,
and the hash set inside each state is a cache miss.
— Hashes of a batch are computed first, destination cells are
prefetched — the aggregator's own trick, applied inside the states.
— Also: hashing aggregate states, JSON, and Variant without a per-row
buffer copy — uniqExact of 200-byte states is 33% faster.
Developers: Manuel.
The right side of a hash join is stored column by column; building
the output gathers one value per column per matched row.
before: col1 Int32 โ col2 String โ col3 UInt8 โ col4 Float64 โ 4 gathers per row
after: ROW STORE (col1 + col3 + col4, contiguous per row) col2 String (columnar)
โโโโโโโโฌโโโโโโโฌโโโโโโโโโ
โ col1 โ col3 โ col4 โ <- row N: one pointer, one copy
โโโโโโโโดโโโโโโโดโโโโโโโโโ
— Fixed-size columns are packed into a row store; on by default
(enable_hash_join_row_store).
— LEFT SEMI / ANTI joins that select no right column build a keys-only
hash table and release the right blocks — up to 1.5x, half the memory.
Example: 100M × 30M LEFT SEMI JOIN: 26.8: 0.50 sec. 26.9: 0.37 sec.
Developers: Hechem Selmi, Nikita Taranov.
— GROUP BY without aggregate functions started prefetching at a much
higher cardinality than GROUP BY with aggregates. Now both start where
the hash table stops fitting in L2.
— The adaptive aggregator (26.8) falls back to the ordinary algorithm
when keys or arguments are too wide to copy profitably — heavy
string columns run at the ordinary speed and memory.
— The bucket top-K optimization (each two-level bucket keeps its best n
groups for ORDER BY … LIMIT n) is visible in EXPLAIN and has a setting:
query_plan_aggregation_bucket_top_k.
Developers: Nikita Taranov, Nihal Z. Miaji.
— Merges: no bitmap allocation for embedded and small posting
lists, no roaring conversion when writing the output list.
— Lazy posting lists: hasToken, hasAnyTokens, hasAllTokens intersect
and unite posting lists block by block, without materializing bitmaps.
— Phrase search: positions in packed blocks; only the blocks
that cover candidate rows are read.
— New: the keyValuePairs tokenizer answers map['key'] = 'value' from the index;
splitByRegexp(re, 1) extracts tokens by a capture group.
Developers: Anton Popov, Elmi Ahmadov, Jimmy Aguilar Mena.
Select rows by where they are in the stream, not by position:
SELECT number FROM numbers(10) ORDER BY number
LIMIT 3 AFTER number >= 5; -- 5, 6, 7
SELECT number FROM numbers(10) LIMIT UNTIL number >= 3; -- 0, 1, 2
SELECT number FROM numbers(10)
LIMIT AFTER number >= 2 UNTIL number >= 6; -- 2, 3, 4, 5
SELECT number FROM numbers(10)
LIMIT 2 AFTER number IN (2, 6) ALL; -- 2, 3, 6, 7
— "From the first error until the next checkpoint" — in one pass,
instead of window functions or client-side cuts.
— AFTER is inclusive, UNTIL exclusive
— conditions may use unselected columns.
Developer: Zakhar Kravchuk, Nihal Miaji.
DISTINCT keeps a hash set of every key — and ran out of memory
on high cardinality. Now it spills to disk, like GROUP BY and ORDER BY do:
SELECT count() FROM (SELECT DISTINCT number FROM numbers_mt(300000000))
SETTINGS max_memory_usage = 3000000000;
-- 26.8: Query memory limit exceeded (MEMORY_LIMIT_EXCEEDED)
SET max_bytes_before_external_distinct = 1000000000;
-- default: max_bytes_ratio_before_external_distinct = 0.5
Example, 300M distinct keys: in memory: 12.9 GB, 12.3 sec.
spilling at 1 GB: 1.14 GB, 12.1 sec. — the same speed, a tenth of the memory.
— On by default. Sorted runs on disk, deduplicated at every stage.
— DISTINCT after ORDER BY keeps the sorted order when it spills.
Developer: Nihal Z. Miaji.
One user, several credentials — each with its own limit:
ALTER USER app ADD IDENTIFIED WITH sha256_password BY 'readonly_key'
VALID FOR INTERVAL 30 DAY
GRANTS (SELECT ON default.sales);
-- a session authenticated with readonly_key:
SELECT count() FROM default.sales; -- 1000
INSERT INTO default.sales VALUES (1, 'x', 1); -- ACCESS_DENIED
— Session rights = the user's grants intersected with the list.
The clause never adds rights.
— Tied to the user: shown as the user in query_log;
when the user is deleted, the auth is refused;
narrows when the user loses grants.
Developer: Alexey Milovidov.
Tokens for applications - generated by the server, for the current user:
GRANT CREATE TOKEN ON *.* TO app;
-- as app:
CREATE TOKEN VALID FOR INTERVAL 7 DAY GRANTS (SELECT ON default.sales);
โโtokenโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโvalid_untilโโ
โ sbnvAg6tBx306T8mrhtLaCxwhiEKTktq โ 2026-09-24 00:25:21 โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโ
$ clickhouse client --user app --password sbnvAg6tBx306T8mrhtLaCxwhiEKTktq
— Shortcut for ALTER USER <me> ADD IDENTIFIED WITH sha256_password BY '<random>'
— Can be run as simple as CREATE TOKEN.
Developer: Alexey Milovidov.
Refreshable materialized views can process only the new rows
committed to the source since the previous refresh:
CREATE TABLE src (ts DateTime, k UInt32, v UInt64) ENGINE = MergeTree ORDER BY ts
SETTINGS enable_block_number_column = 1, enable_block_offset_column = 1;
CREATE MATERIALIZED VIEW hourly
REFRESH EVERY 1 HOUR APPEND INCREMENTAL ENGINE = MergeTree ORDER BY k
AS SELECT k, sum(v) AS s, count() AS c FROM src GROUP BY k;
— The cursor is the source's block numbers — hence the two settings.
— Between the incremental MV (per insert) and a full refresh:
takes the delta from the source, on a schedule.
Developer: Smita Kulkarni.
Refreshable materialized views can process only the new rows
committed to the source since the previous refresh:
CREATE TABLE src (ts DateTime, k UInt32, v UInt64) ENGINE = MergeTree ORDER BY ts
SETTINGS enable_block_number_column = 1, enable_block_offset_column = 1;
CREATE MATERIALIZED VIEW hourly
REFRESH EVERY 1 HOUR APPEND INCREMENTAL ENGINE = MergeTree ORDER BY k
AS SELECT k, sum(v) AS s, count() AS c FROM src GROUP BY k;
A use-case: replicating MergeTree tables into Iceberg.
Append into an Iceberg target is exactly-once: the cursor is in the snapshot.
Developer: Smita Kulkarni.
A TimeSeries table speaks the Prometheus HTTP API — point Grafana at it:
$ cat config.d/prometheus_api.yaml
http_handlers:
defaults: {}
rule:
url_prefix: /prometheus/api/v1
handler: { type: prometheus_api_v1, database: default, table: metrics }
$ curl --get 'http://server:8123/prometheus/api/v1/query' \
--data-urlencode 'query=sum by (status) (rate(http_requests_total[5m]))'
{"status":"success","data":{"resultType":"vector","result":[
{"metric":{"status":"500"},"value":[1789606535.825,"1.52"]},
{"metric":{"status":"200"},"value":[1789606535.825,"1.39"]}]}}
— New in 26.9: /metadata, /labels, /label/<name>/values, /format_query;
and recent /query, /query_range, /series, /write, /read.
Developers: Nikita Mikhaylov, Minh Vu, Vitaly Baranov.
The TimeSeries engine, the PromQL dialect, and the timeSeries* functions
moved from experimental to private preview — a drop-in Prometheus replacement:
SET dialect = 'promql', promql_database = 'default', promql_table = 'metrics';
sum by (status) (rate(http_requests_total[5m]))
-- [('status','500')] โ 2026-09-17 00:56:28.000 โ 1.245
-- or from SQL, with a time range and a step:
SELECT tags, samples FROM prometheusQueryRange(default.metrics,
'sum by (status) (rate(http_requests_total[5m]))',
now() - INTERVAL 1 HOUR, now(), INTERVAL 5 MINUTE);
— 85%+ of PromQL with exact Prometheus semantics; 26.9 adds absent,
count_values, *_over_time, predict_linear, SELECT from TimeSeries tables.
— Blog: Introducing ClickHouse's new TimeSeries engine.
Developers: Vitaly Baranov, Nikita Mikhaylov, Valery Petrov, Minh Vu.
The KQL dialect was contributed in 2022 and stalled: it translated
tokens into SQL text and reparsed it,
28 functions registered that did nothing.
SET dialect = 'kusto', allow_experimental_kusto_dialect = 1;
print q = 7 / 2, sub = substring('abcdefg', -3, 2), c = '50x' contains '50%'
-- before: 3.5 โ 'ab' โ true (the needle was pasted into a LIKE pattern)
-- now: 3 โ 'ef' โ false โ what Kusto returns
— Now a dedicated lexer, parser, and AST translation.
Developer: Alexey Milovidov.
Queries written for Trino (and Presto, Athena) run as they are:
SET dialect = 'trino', enable_trino_dialect = 1;
SELECT x, TRY_CAST('abc' AS INTEGER) AS c, cardinality(ARRAY[1, 2, 3]) AS n
FROM UNNEST(ARRAY[10, 20, 30]) AS t(x) OFFSET 1 LIMIT 5;
-- 20 โ NULL โ 3 / 30 โ NULL โ 3
SELECT transform(ARRAY[1, 2, 3], x -> x * 2), approx_percentile(v, 0.9),
date_trunc('month', TIMESTAMP '2026-09-17 10:00:00'), ROW(1, 'a')
FROM (VALUES (1), (2), (3), (10)) AS t(v);
-- [2,4,6] โ 10 โ 2026-09-01 โ (1,'a')
— Not a separate grammar: token-level syntax translation, the standard parser,
and an AST-level mapping of ~340 Trino functions (renames, lambdas last).
Developers: Alexey Milovidov, Ethan Lin.
Reading Delta Lake since 23.x, writing since 26.2 — now ClickHouse
can create tables from scratch:
SET allow_delta_lake_writes = 1, allow_delta_lake_create_table = 1;
CREATE TABLE trips (id Int64, name String, score Float64)
ENGINE = DeltaLake('s3://bucket/warehouse/trips/', '<key>', '<secret>');
INSERT INTO trips VALUES (1, 'a', 1.5), (2, 'b', 2.5);
-- _delta_log/00000000000000000000.json:
-- {"commitInfo":{"operation":"CREATE TABLE","engineInfo":"ClickHouse",...}}
— Can also attach to an existing _delta_log or register into a Unity catalog. S3, Azure, local.
Developer: Smita Kulkarni.
Planning a query over Iceberg walks its manifests: fetch, parse Avro,
prune every entry by partition and min/max. That walk ran serially.
— 26.8 prefetched the next manifest — overlapping fetch only.
— 26.9 decodes manifests concurrently, streaming the surviving
entries into a bounded queue with backpressure.
Example (from the PR): 18 data manifests, 113 MB, ~75K entries:
serial: 8.45 sec. 4 threads: 2.55 sec. 16 threads: 1.68 sec.
— iceberg_manifest_decode_concurrency (default 4) bounds it.
Developer: Aaron Harlap.
The manifest list carries per-manifest partition summaries: lower_bound,
upper_bound, contains_null. They now prune whole manifests before any is opened:
CREATE TABLE ice (id Int64, m UInt32, d Date, v Float64)
ENGINE = Iceberg(…) PARTITION BY m; -- 8 inserts, one month each
SELECT count() FROM ice WHERE m = 5;
-- metadata files read: 41 -> 7 (use_iceberg_manifest_list_partition_pruning)
-- data files listed: 39 -> 5 (per-entry partition pruning, as before)
— Three levels now: manifest list, manifest entries, Parquet row groups.
— ClickHouse also writes the partition summaries into the manifest list since 26.9.
Developer: Konstantin Vedernikov.
The SQL Console of ClickHouse Cloud, embedded in the server binary:
http://your-server:8123/ui
— A standalone build that talks directly to the HTTP endpoint
— Similar to ClickHouse Cloud Console.
— The main Web UI stays at /play.
Demo
Developer: Luis Neves.
Eight functions, three releases in the making, no experimental flag anymore:
SELECT title, aiClassify(title, ['database', 'AI', 'security', 'other'])
FROM hackernews WHERE type = 'story' ORDER BY time DESC LIMIT 100;
SELECT aiTranslate(comment, 'German'), aiRedact(comment),
aiSimilarity(comment, 'a complaint about latency') FROM feedback;
— aiGenerate, aiClassify, aiExtract, aiTranslate (26.4), aiEmbed (26.6),
aiFilter, aiRedact, aiSimilarity (26.8) — all beta in 26.9.
— Credentials in named collections; per-query quotas on tokens and calls.
— Blog: AI Functions in ClickHouse: Upgrade your SQL to the AI age.
Developers: George Larionov, Andriy Yakovlev.
— ๐บ๐ธ Chicago Meetup, Sep 28
— ๐ธ๐ฌ Singapore: Build Better LLM Apps, Sep 29
— ๐ซ๐ท Paris: AI Builders and Databases, Sep 29
— ๐ฌ๐ง London: User Conference + Trainings, Sep 30
— ๐ฉ๐ช Munich: User Conference + Trainings, Oct 6
— ๐ณ๐ฟ Auckland: Postgres and ClickHouse, Oct 6
— ๐ง๐ท Sรฃo Paulo: ClickStack Training, Oct 8
— ๐ธ๐ช Stockholm: AI Builders and Databases, Oct 8
— ๐บ๐ธ South Bay Meetup, Oct 8
— ๐ฎ๐ฑ Tel Aviv: AI Builders and Databases, Oct 12
— ๐ฎ๐ช Dublin: SRECon Happy Hour, Oct 13
— ๐ณ๐ด Oslo: Training, Oct 14 ยท ๐ฆ๐บ Melbourne: LLM Apps, Oct 15
— ๐ฌ๐ง London: AI Builders, Oct 21 ยท ๐จ๐พ Limassol, Nov 26
๐ฌ๐ง London, Sep 30 ยท ๐ฉ๐ช Munich, Oct 6
— The TimeSeries engine: a drop-in Prometheus replacement
— AI Functions: upgrade your SQL to the AI age
— ClickHouse is now available on the dbt platform
— Replica-aware routing: public beta
— WalShadow: sub-second Postgres replication
— On-Demand Compute for intensive workloads
— CostBench: performance per dollar under load
— ClickHouse Cloud vs. Snowflake
— chdb Postgres extension