またひと月が過ぎ、新たなリリースの時期がやってきました!
ClickHouse 26.8 リリースには、98 個の新機能 🍇、128 件のパフォーマンス最適化 🌠、556 件のバグ修正 🐝 が含まれており、長期サポート (LTS) リリースとなっています。
本リリースでは、バックグラウンドクエリ、パイプライン化された SQL、新しいテキストトークナイザー、データレイク統合の拡張に加え、Parquet、GROUP BY、JOIN のパフォーマンス向上がもたらされます。
新しいコントリビューター
26.8 で加わったすべての新しいコントリビューターを心から歓迎します!ClickHouse コミュニティの広がりには目を見張るものがあり、ClickHouse をこれほど支持される存在にしてくださった皆様の貢献に常に感謝しています。
新しいコントリビューターのお名前は以下のとおりです。
Aashish Kohli, AbdullahKaya, Alex Budkar, Alex Kalmakov, Alexey Elkin, Amog Iska, Amogh-Bharadwaj, Andrey Tsarevskiy, Andy Bradshaw, Avinash Kamath, Bartok, Bartok9, Chris Lu, Christian Bianchi, ClickGap, Daniel Q. Kim, David Dallakyan, Dean Chen, Dergousov Maksim, Diego Gomes Tomé, Dmitrii Tunikov, Eduardo Gómez, Emil Sadek, George MacRorie, Hamid, Hank Cui, Haowen Feng (from Dev Box), Ilia Demianenko, Ivan Shelestov, James Sanders, Jose Muñoz, KD2YCU, Kirill Shcherbatov, Kirill Shokhin, Konstantin Plis, Kseniia, Nikolai Ovchinnikov, Nishant, Nishant Agarwal, Nuno Adrego, Perfloop Agent, Rahul Malik, RamiDarwiche, Raymond Lee, Ria Khatoniar, Ria-K912, Rohith Pariki, RohithPariki, Schum, Sean Reid, Sergey Chernov, Shawn Chen, Stanislav, Steve Lerner, Tod Trevillian, UberDever, Utkal Singh, Valery Petrov, VighneshPath, Vladimir Chemeris, William Hatcher, Yecine Megdiche, Yiyang Shao, ZachEddy, Zeynel Koca, a.akhondi, addshore, amirreza1307, avinash, deusgaudio, francisconeves-clickhouse, gelsonbagetti, gudauu, kalyanamdewri, locadex-agent[bot], pavol kutaj, quantrail-admin, vahid, vahid sohrabloo, yisamlee, yiyang-shao, zainulabidin302, Éco
ヒント: このリストをどのように生成しているか気になる方は… こちらをご覧ください。
プレゼンテーションのスライドもあわせてご覧いただけます。
バックグラウンドでのクエリ実行
貢献者: Miсhael Stetsyuk
クエリをバックグラウンドで実行できるようになりました。クエリは即座に応答を返し、接続が切断されても完了するまで実行され続けます。
この機能は、ClickHouse へのデータ取り込みや ClickHouse からのデータエクスポートといった長時間実行クエリに有用で、クライアントが切断しても処理を継続できます。
英国の不動産価格データセットをインポートした ClickHouse サーバーを用意し、以下のクエリを使ってデータを数回複製します。
ALTER TABLE uk_price_paid
ATTACH PARTITION ID 'all'
FROM uk_price_paid;これにより、2 億 4,000 万行強のデータが作成されます。
SELECT count() FROM uk_price_paid;┌───count()─┐
│ 243619704 │
└───────────┘次に、以下のクエリを実行して、uk_price_paid テーブルのデータをローカル HTTP サーバー上のファイルに書き出します。
INSERT INTO FUNCTION url('http://localhost:8080/uploads/london-sales.parquet', 'Parquet')
SELECT * FROM uk_price_paid
SETTINGS run_query_in_background = 1;Query id: 59eabb9f-33f4-412b-b2f3-b090fe7b4bbf
Ok.
0 rows in set. Elapsed: 0.001 sec.system.processes をクエリすることで、バックグラウンドクエリの進行状況を確認できます。
SELECT query_id, query,
round(elapsed, 2) AS elapsed_seconds,
read_rows, written_rows
FROM system.processes
WHERE query NOT ILIKE '%system.processes%'
ORDER BY elapsed DESC;Row 1:
──────
query_id: 59eabb9f-33f4-412b-b2f3-b090fe7b4bbf
query: INSERT INTO FUNCTION url('http://localhost:8080/uploads/uk.parquet', 'Parquet')
SELECT * FROM uk_price_paid
SETTINGS run_query_in_background = 1;
elapsed_seconds: 8.52
read_rows: 179243204 -- 179.24 million
written_rows: 179243204 -- 179.24 millionクエリが完了したら、system.query_log をクエリして所要時間を確認できます。
SELECT event_time, type, query_duration_ms,
read_rows, result_rows
FROM system.query_log
WHERE query_id = '59eabb9f-33f4-412b-b2f3-b090fe7b4bbf'
ORDER BY event_time;┌──────────event_time─┬─type────────┬─query_duration_ms─┬─read_rows─┬─result_rows─┐
│ 2026-09-03 12:32:05 │ QueryStart │ 0 │ 0 │ 0 │
│ 2026-09-03 12:32:17 │ QueryFinish │ 11798 │ 243619704 │ 243619704 │
└─────────────────────┴─────────────┴───────────────────┴───────────┴─────────────┘PostgreSQL スタイルの正規表現演算子
貢献者: Alexey Milovidov
ClickHouse は、以下の PostgreSQL スタイルの正規表現演算子をサポートするようになりました。
~: 文字列が正規表現に大文字小文字を区別して一致する場合に 1 を返します。~*: 文字列が正規表現に大文字小文字を区別せずに一致する場合に 1 を返します。!~: 文字列が正規表現に大文字小文字を区別して一致しない場合に 1 を返します。!~*: 文字列が正規表現に大文字小文字を区別せずに一致しない場合に 1 を返します。
以下のクエリは、これらの演算子すべてを使用して Baker Street という語の一部にマッチさせる方法を示しています。
SELECT
'Baker Street' ~ 'street$' AS matchesCaseSensitively,
'Baker Street' ~* 'street$' AS matchesCaseInsensitively,
'Baker Street' !~ 'street$' AS doesNotMatchCaseSensitively,
'Baker Street' !~* 'street$' AS doesNotMatchCaseInsensitively
FORMAT Vertical;Row 1:
──────
matchesCaseSensitively: 0
matchesCaseInsensitively: 1
doesNotMatchCaseSensitively: 1
doesNotMatchCaseInsensitively: 0補足: この演算子を実装した Alexey から注記があります。彼はこれらをあまり好んでおらず、SQL が Bash や Perl に似すぎていると感じています。今回は PostgreSQL の互換性が優先されました。 この変更に伴い、PostgreSQL ワイヤープロトコル経由で ClickHouse に接続した際にも
\d、\dt、\dvなどのpsqlコマンドが動作するようになりました。ただし、\d <table>は依然として ClickHouse が未対応の PostgreSQL 構文を使用しています。
配列による配列の添字指定
貢献者: folly
添字演算子でインデックスの配列を受け取れるようになり、指定されたすべての位置にある要素を返せるようになりました。これにより、配列要素の抽出、並べ替え、サンプリングを単一の式で実行できるようになり、arraySort や topK 系の関数と併用する際に便利です。
簡単な例を見てみましょう。
WITH arrayMap(x -> rand(x), range(1, 10)) AS random_values
SELECT random_values[[1, 3, 5]];┌─arrayElement(ran⋯ues, [1, 3, 5])─┐
│ [344408953,1157293782,407846805] │
└──────────────────────────────────┘より実践的な用途として、月次の時系列データから四半期ごとのチェックポイントを抽出する例があります。以下のクエリは、各地区の月次中央値価格のソート済み配列を作成し、1 つの式で 1 月、4 月、7 月、10 月を選択します。
WITH monthlyPrices AS
(
SELECT district, toStartOfMonth(date) AS month,
round(median(price)) AS medianPrice
FROM uk_price_paid
WHERE town = 'LONDON'
AND district IN ('CAMDEN', 'CITY OF WESTMINSTER', 'KENSINGTON AND CHELSEA')
AND date >= '2024-01-01' AND date < '2025-01-01'
GROUP BY district, month
)
SELECT district,
arraySort(groupArray((month, medianPrice)))[[1, 4, 7, 10]] AS quarterlyPrices
FROM monthlyPrices
GROUP BY district
HAVING count() = 12
ORDER BY district;Row 1:
──────
district: CAMDEN
quarterlyPrices: [('2024-01-01',760000),('2024-04-01',731000),('2024-07-01',762500),('2024-10-01',805000)]
Row 2:
──────
district: CITY OF WESTMINSTER
quarterlyPrices: [('2024-01-01',1215000),('2024-04-01',1037500),('2024-07-01',935000),('2024-10-01',875000)]
Row 3:
──────
district: KENSINGTON AND CHELSEA
quarterlyPrices: [('2024-01-01',1205000),('2024-04-01',1187500),('2024-07-01',1100000),('2024-10-01',1200000)]クエリから JSON へ、JSON からクエリへ
貢献者: Alexey Milovidov, Nikita Fomichev
クエリを JSON 形式の抽象構文木 (AST) に変換すること、および JSON 形式の抽象構文木を使用して ClickHouse をクエリすることが可能になりました。
parseQueryToJSON 関数は、JSON 形式で抽象構文木を返します。出力が非常に詳細になるため、この関数のデモには単純な count() を使用します。
SELECT parseQueryToJSON($sql$
SELECT count() FROM uk_price_paid
$sql$)::JSON
FORMAT Vertical;{
"type": "SelectWithUnionQuery",
"union_mode": "UNION_DEFAULT",
"list_of_selects": {
"type": "ExpressionList",
"children": [
{
"type": "SelectQuery",
"select": {
"type": "ExpressionList",
"children": [
{"type": "Function", "name": "count",
"arguments": {"type": "ExpressionList"}}
]
},
"tables": {
"type": "TablesInSelectQuery",
"children": [
{"type": "TablesInSelectQueryElement",
"table_expression": {
"type": "TableExpression",
"database_and_table_name": {
"type": "TableIdentifier", "name": "uk_price_paid"}}}
]
}
}
]
}
}この JSON は、識別子、リテラル、関数、句といった型付きノードのツリーとしてクエリを記述します。
通常の JSON であるため、JSON ライブラリを持つあらゆる言語で、SQL パーサーを組み込むことなくクエリの解析、検証、書き換え、生成を行えます。
変換は双方向に対応しています。formatQueryFromJSON はツリーを SQL に戻すため、構築した内容をいつでも確認できます。
SELECT formatQueryFromJSON(parseQueryToJSON($sql$
SELECT count() FROM uk_price_paid
$sql$));┌─formatQueryFromJ⋯_price_paid\n'))─┐
│ SELECT count() FROM uk_price_paid │
└───────────────────────────────────┘ClickHouse 26.8 には、SQL の代わりに JSON AST を送信できる実験的方言 clickhouse_json も追加されました。この機能は enable_json_ast_dialect 設定によって有効化され、他の方言と同様に選択できます。
SET enable_json_ast_dialect = 1, dialect = 'clickhouse_json';先ほど作成した SELECT count() FROM uk_price_paid の AST を貼り付けると、以下の結果が得られます。
┌───count()─┐
│ 243619704 │
└───────────┘ユーザーが手作業でクエリを JSON 構文木として書き始めることは想定していません。この機能は人間向けというよりも機械向けです。
クエリを生成するあらゆる仕組みが、SQL の代わりに JSON 構文木を生成できるようになります。これにより、文字列連結が不要になり、厄介なエスケープのバグが解消され、そしておそらく最も重要な点として、SQL インジェクションの攻撃対象領域が排除されます。
CREATE USER ... VALID FOR
貢献者: Alexey Milovidov
ClickHouse 26.8 より前でも、VALID UNTIL 構文を使用して有効期限付きのユーザーを作成できました。
CREATE USER mark
IDENTIFIED WITH no_password
VALID UNTIL '2026-10-04';ClickHouse 26.8 では VALID FOR INTERVAL 構文が追加され、現在時刻を起点として期限を計算できるようになりました。したがって、現在から 2 週間有効なユーザーを作成するには、以下のクエリを実行します。
CREATE USER mark
IDENTIFIED WITH no_password
VALID FOR INTERVAL 2 WEEKS;その後、このユーザーの有効期限を次のクエリで確認できます。
SHOW CREATE USER mark
FORMAT LineAsString;このクエリの出力はすべて 1 行で表示されるため、読みやすさのために手動でフォーマットしています。
CREATE USER mark
IDENTIFIED WITH no_password
VALID UNTIL '2026-09-18 10:06:29'この構文はユーザーの変更時にも使用できます。ユーザーの有効期限を現在から 1 週間に変更するには、以下のクエリを実行します。
ALTER USER mark VALID FOR INTERVAL 1 WEEK;CREATE USER mark
IDENTIFIED WITH no_password
VALID UNTIL '2026-09-11 10:08:34'パイプライン化された SQL
貢献者: Alexey Milovidov
ClickHouse 26.8 ではパイプライン化された SQL が導入され、各ステップを前のステップに続ける形で、クエリを上から下へと記述できるようになりました。
詳細については、ブログ記事「ClickHouse 26.8 におけるパイプライン化された SQL」をご覧ください。
ストリーミング HTTP API としての ClickHouse
貢献者: Alexey Milovidov
ClickHouse 26.8 では、テーブルや一般的なクエリに対して軽量な HTTP API を手軽に追加できる機能もいくつか導入されました。
詳細については、ブログ記事「ストリーミング HTTP API としての ClickHouse」をご覧ください。
system.user_query_log
貢献者: Yue Ni, Alexey Milovidov
ClickHouse 26.8 では、現在のユーザーのクエリのみを含む新しいシステムテーブル system.user_query_log も導入されました。
これにより、すべてのユーザーが system.query_log テーブルへのアクセス権を持たずに、自身のクエリ履歴を確認できるようになります。
なお、ユーザーが system.query_log へのアクセス権限を持っていない場合、同テーブルへのクエリは引き続き権限拒否の例外を返します。
アトミックな POPULATE
貢献者: Alexey Milovidov
インクリメンタルマテリアライズドビューは、データブロックがテーブルに挿入されるたびにクエリを実行するトリガーと同等です。
インクリメンタルマテリアライズドビューを作成し、作成時にデータを投入 (POPULATE) する際、競合状態によって投入中に挿入されたレコードがスキップされる現象が発生していました。ClickHouse 26.8 では、これが修正されました。
CREATE MATERIALIZED VIEW ... POPULATE を呼び出すと、ソーステーブルに対する短い排他ロックの下で、ソーステーブルへの新規挿入の購読と既存データのスナップショット取得が同時に行われ、データ投入と並行して挿入されたすべての行が正確に 1 回だけ配信されます。
この機能はデフォルトで有効になっていますが、materialized_views_populate_atomically を 0 に設定することで無効化し、以前の動作を再現できます。
まず、100,000 行を含むソーステーブルを作成します。
CREATE TABLE src (id UInt64)
ORDER BY id;
INSERT INTO src
SELECT number FROM numbers(100000);次に、送信先テーブルを作成します。
CREATE TABLE dst (id UInt64)
ORDER BY id;次に、一方のクエリで 50 行を挿入し、もう一方のクエリで dst テーブルにデータを投入するマテリアライズドビューを作成します。
行が漏れるには、POPULATE がソーステーブルのスナップショットを取得している最中に、その行の INSERT がまだ処理中である必要があります。通常の挿入は処理が速すぎてそうした状況になりにくいため、sleepEachRow を使用して 50 行の挿入を約 2.5 秒間に分散させ、その挿入が実行されている間に CREATE を開始します。
ここでは POPULATE と TO を併用していますが、これは 26.8 より前は構文エラーとなっていました。このビューは、src にすでに存在するデータから既存の dst テーブルへのバックフィルを行います。
./clickhouse client -q "
INSERT INTO src SELECT number + 100000 FROM numbers(50)
WHERE sleepEachRow(0.05) = 0;" &
sleep 0.5
./clickhouse client -m -q "
SET materialized_views_populate_atomically = 0;
CREATE MATERIALIZED VIEW mv TO dst
POPULATE AS SELECT id FROM src;"
wait実行が完了したら、ソーステーブルと送信先テーブルを比較します。
SELECT (SELECT count() FROM src) AS sourceRows,
(SELECT count() FROM dst) AS dstRows,
(SELECT uniqExact(id) FROM dst) AS dstDistinct,
sourceRows - dstRows AS lost,
dstRows - dstDistinct AS duplicated
FORMAT PrettyCompact;┌─sourceRows─┬─dstRows─┬─dstDistinct─┬─lost─┬─duplicated─┐
│ 100050 │ 100000 │ 100000 │ 50 │ 0 │
└────────────┴─────────┴─────────────┴──────┴────────────┘50 件のレコードはすべてソーステーブルに存在しますが、送信先テーブルには 1 件もありません。挿入は mv が存在する前に開始されたためビューにはプッシュされず、投入用スナップショットが取得された後にコミットされたため、バックフィルでも認識されませんでした。
行ごとやブロックごとではなく、クエリの開始時に挿入がどのビューにデータを送るかを一度だけ決定するため、ちょうど 50 件となります。そのため、挿入全体が届くか届かないかのどちらかになります。
mv、src、dst を削除してから src と dst を再作成し、今度は materialized_views_populate_atomically を 1 に設定してマテリアライズドビューを作成します。
./clickhouse client -q "
INSERT INTO src SELECT number + 100000 FROM numbers(50)
WHERE sleepEachRow(0.05) = 0;" &
sleep 0.5
./clickhouse client -m -q "
SET materialized_views_populate_atomically = 1;
CREATE MATERIALIZED VIEW mv TO dst
POPULATE AS SELECT id FROM src;"
wait今回は、各テーブルのレコード数をカウントすると以下の出力が得られます。
┌─sourceRows─┬─dstRows─┬─dstDistinct─┬─lost─┬─duplicated─┐
│ 100050 │ 100050 │ 100050 │ 0 │ 0 │
└────────────┴─────────┴─────────────┴──────┴────────────┘日本語および中国語トークナイザーのサポート
貢献者: Robert Schulze, Amos Bird, Jimmy Aguilar Mena
ClickHouse のデフォルトのトークナイザー(tokens、hasAllTokens、hasAnyTokens 関数で使用)は splitByNonAlpha であり、空白文字や句読点で文字列を分割して部分文字列の配列にします。英語や他のインド・ヨーロッパ語族とは異なり、中国語や日本語の文章には単語間のスペースがないため、専用のトークナイザーが必要となります。
26.8 では、以下の 2 つの専用トークナイザーによってこれに対応しました。
- japanese - MeCab 形態素解析器を利用する日本語トークナイザー。サーバー設定で指定された外部の MeCab 辞書を必要とします
- chinese - jieba スタイルのトークナイザー。辞書と HMM (隠れマルコフモデル) を組み合わせて未知の連続文字列を分割します。例えば「ClickHouse是一个快速的开源数据库」を 1 つの塊や 1 文字ずつではなく、「ClickHouse」「是」「一个」「快速」「的」「开源」「数据库」に分割します。
日本語
日本語トークナイザーを使用するには、まず辞書を設定する必要があります。この例では UniDic を使用します。Web サイトから .zip アーカイブをダウンロードしたら、アーカイブの SHA256 を取得する必要があります。ClickHouse は辞書をロードする前にこれを使用して検証を行います。
sha256sum /var/lib/clickhouse/unidic-cwj-202512.zipd94216b589d15d05c408ed59abc5259086703ebbac14e225b5314e4cd106c4db/etc/clickhouse-server/config.d/tokenizers.xml に新しいトークナイザーを追加し、サーバー起動時にプライマリの config.xml ファイルにマージされるカスタム設定を定義します。
<clickhouse>
<tokenizer>
<japanese>
<dictionary_location>file:///var/lib/clickhouse/unidic-cwj-202512.zip</dictionary_location>
<dictionary_sha>d94216b589d15d05c408ed59abc5259086703ebbac14e225b5314e4cd106c4db</dictionary_sha>
</japanese>
</tokenizer>
</clickhouse>Note
HTTP/HTTPS URL 経由、または S3 互換ストレージ内の辞書を指定することも可能です
サーバーがすでに実行中の場合は、設定を反映するために再起動してください。これで、テキストインデックスを作成し、以下のようにクエリを実行できるようになります。
CREATE TABLE reviews
(
id UInt64,
text String,
INDEX text_idx text TYPE text(tokenizer = 'japanese')
)
ENGINE = MergeTree
ORDER BY id;
INSERT INTO reviews VALUES
(1, '渋谷の新しい寿司屋で美味しいうにを食べた'), -- "ate" (食べた)
(2, '大阪のラーメンは最高だった、また食べたい'), -- "want to eat" (食べたい)
(3, '京都で抹茶アイスを食べながら散歩した'), -- "while eating" (食べながら)
(4, '新幹線に乗って富士山を見に行った'); -- no mention of eating
SELECT id, text
FROM reviews
WHERE hasAnyTokens(text, ['食べ'], 'japanese')
ORDER BY id;| id | text |
|---|---|
| 1 | 渋谷の新しい寿司屋で美味しいうにを食べた |
| 2 | 大阪のラーメンは最高だった、また食べたい |
| 3 | 京都で抹茶アイスを食べながら散歩した |
3 rows in set. Elapsed: 0.007 sec.
上記の例では、食べた、食べたい、食べながら はいずれも動詞 食べる の異なる活用形ですが、トークナイザーはそれぞれを語幹の 食べ と個別の活用助詞に分割します。そのため、食べ という単一トークンの検索で 1〜3 行目が正しく取得され、4 行目はスキップされます。
詳細については、日本語トークナイザーのドキュメントをご覧ください。
中国語
外部の辞書アーカイブをダウンロードしてサーバーの XML 設定ファイルに記述する必要がある日本語トークナイザーとは異なり、中国語トークナイザーではサーバー設定のセットアップは不要です。組み込みの辞書および隠れマルコフモデルのデータ (cppjieba 由来) は、ClickHouse に直接組み込まれています。
chinese トークナイザーでは粒度パラメータを指定できます。デフォルトでは coarse_grained に設定されていますが、fine_grained を渡すと重複するサブワードをインデックス化でき、インデックスサイズが増大する代わりに検索再現率を向上させることができます。
以下のクエリは 2 つのテーブルを作成します。1 つ目のテーブルはデフォルトの coarse_grained 粒度パラメータを持つ chinese トークナイザーを使用したインデックスを持ち、2 つ目のテーブルは fine_grained パラメータを使用したインデックスを持ちます。
両方のテーブルに 4 つの文字列が挿入されます。これらには、大学 という単語が単独で含まれるもの (1 行目)、より長い複合語の中に埋め込まれているもの (2 行目および 3 行目)、まったく含まれないもの (4 行目) があります。
CREATE TABLE table_coarse
(
key UInt64,
str String,
INDEX text_idx str TYPE text(tokenizer = chinese) -- default: coarse_grained
)
ENGINE = MergeTree ORDER BY key;
CREATE TABLE table_fine
(
key UInt64,
str String,
INDEX text_idx str TYPE text(tokenizer = chinese('fine_grained'))
)
ENGINE = MergeTree ORDER BY key;
INSERT INTO table_coarse VALUES
(1, '他考上了大学,很开心'), -- standalone 大学
(2, '北京邮电大学的通信工程专业很强'), -- compound 北京邮电大学
(3, '我毕业于北京大学计算机系'), -- compound 北京大学
(4, '今天天气不错,适合散步'); -- no mention
INSERT INTO table_fine SELECT * FROM table_coarse;以下のクエリは、hasAllTokens 関数を使用して 大学 を検索します。まず chinese インデックスでデフォルトの coarse_grained パラメータを使用しているテーブルに対して実行し、次に fine_grained パラメータを使用している 2 つ目のテーブルに対して実行します。
1 つ目のテーブルでは、大学 が単独で言及されている文である 他考上了大学,很开心 (「彼は大学に合格してとても喜んでいた」) のみが返されます。2 つ目のテーブルでは、大学 が単独で言及されている文と、複合語として言及されている文の両方が返されます。
SELECT
key,
str
FROM table_coarse
WHERE hasAllTokens(str, '大学')
ORDER BY key ASC;Query ID: 7e348059-9cd1-4f72-8026-8d76266050e4
| key | str |
|---|---|
| 1 | 他考上了大学,很开心 |
1 row in set. Elapsed: 0.002 sec.
SELECT
key,
str
FROM table_fine
WHERE hasAllTokens(str, '大学')
ORDER BY key ASC;Query ID: 4da23c89-8a10-4099-9637-bd2a14f6aa1b
| key | str |
|---|---|
| 1 | 他考上了大学,很开心 |
| 2 | 北京邮电大学的通信工程专业很强 |
| 3 | 我毕业于北京大学计算机系 |
3 rows in set. Elapsed: 0.002 sec.
これは、chinese トークナイザーが粗い (coarse) モードでは 北京大学 や 北京邮电大学 を単一の辞書トークンとして扱うためです。そのため、人間が文を読めばどちらの文も「大学」に該当することが分かりますが、粗いトークン化ではサブトークンの「大学」単体での検索がそれらの行に一致することはありません。fine_grained モードは、こうした複合語を 北京、邮电、大学 といった重複するサブワードにさらに分割する設定であるため、2 行目と 3 行目もヒットするようになります。
新しいトークナイザー: icu と splitByRegexp
貢献者: Jimmy Aguilar Mena
Jieba は簡体字および繁体字中国語向けに設計されており、MeCab は主に日本語を対象としています。一方、ICU (International Components for Unicode) は標準的な境界分析アルゴリズムを使用する汎用的なルールベースの多言語ライブラリですが、タイ語、ラオス語、クメール語、ビルマ語のように単語間に空白を置かない他の言語のテキストをどのようにトークン化すべきでしょうか。
バージョン 26.8 では、新しい icu(locale) トークナイザーが ClickHouse に追加されました。このトークナイザーは、ICU ライブラリの Unicode 単語分割機能を使用して文字列を単語トークンに分割します。単語間に空白を置かない表記体系に対して、ICU は辞書ベースの分割を適用するため、テキストは単一文字ではなく意味のある複数文字の単語に確実に分割されます。
26.8 より前は、asciiCJK のような非 ASCII トークナイザーを使ってタイ語、ラオス語、クメール語、ビルマ語のテキストをトークン化しようとすると、asciiCJK がすべての非 ASCII 文字を個別のトークンとして扱うため、1 文字ごとに断片化する問題が発生していました。例えば、タイ語の บ้าน (「家」) を見てみましょう。
SELECT tokens('บ้าน', 'asciiCJK');┌─tokens('บ้าน', 'asciiCJK')─┐
│ ['บ','้','า','น'] │
└───────────────────────────┘บ と น は子音、้ は声調記号、า は母音記号であるため、トークン化によって単語が無意味な 4 つの断片に細分化されてしまいます。hasAllTokens は各検索対象の断片が行内のどこかに存在するかどうかだけをチェックするため、まったく無関係な 2 つのタイ語の単語がそれぞれ断片を提供し、「馬は山の上にいる」という完全に無関係な文に対する「家」の検索で誤検知を引き起こす可能性があります。
-- "ม้าอยู่บนภูเขา" = "The horse is on the mountain" (no usage of "house")
SELECT tokens('ม้าอยู่บนภูเขา', 'asciiCJK');┌─tokens('ม้าอยู่บนภูเขา', 'asciiCJK')──────────────────────┐
│ ['ม','้','า','อ','ย','ู','่','บ','น','ภ','ู','เ','ข','า'] │
└───────────────────────────────────────────────────────┘SELECT hasAllTokens('ม้าอยู่บนภูเขา', 'บ้าน', 'asciiCJK');┌─hasAllTokens⋯'asciiCJK')─┐
│ 1 │
└──────────────────────────┘
-- false positiveこれを、locale を th に設定した icu トークナイザーを使用した場合の動作と比較してください。
SELECT tokens('ม้าอยู่บนภูเขา', 'icu', 'th');┌─tokens('ม้าอยู่⋯icu', 'th')─┐
│ ['ม้า','อยู่','บน','ภูเขา'] │
└──────────────────────────┘
-- "horse", "is/located", "on", "mountain"SELECT hasAllTokens('ม้าอยู่บนภูเขา', 'บ้าน', 'icu(''th'')');┌─hasAllTokens⋯u(\'th\')')─┐
│ 0 │
└──────────────────────────┘Tip
サポートされているすべての ICU ロケールの一覧は、system.collations テーブルをクエリして確認できます。
より詳細な制御が必要な状況向けに、26.8 では区切り文字として正規表現を使用してテキストをトークンに分割できる splitByRegexp トークナイザーが導入されました。
ClickHouse のデフォルトのトークナイザーである splitByNonAlpha は、英数字以外のすべての ASCII 文字で分割するため、C++、C#、F# のようなテキストはすべて同じ単一の文字に縮約されてしまいます。
SELECT
tokens('C++', 'splitByNonAlpha'),
tokens('C#', 'splitByNonAlpha'),
tokens('F#', 'splitByNonAlpha')
FORMAT Vertical;tokens('C++'⋯yNonAlpha'): ['C']
tokens('C#',⋯yNonAlpha'): ['C']
tokens('F#',⋯yNonAlpha'): ['F']実際には、これは「C#」の検索で「I am an expert in C++」のような誤検知が返される可能性があることを意味します。
SELECT hasAllTokens('I am an expert in C++', 'C#', 'splitByNonAlpha');┌─hasAllTokens⋯yNonAlpha')─┐
│ 1 │
└──────────────────────────┘さらに、hasToken は区切り文字を含む検索語を拒否するため、splitByNonAlpha ではリテラル用語である C++ や C# を検索する手段がまったくありません。
splitByRegexp を使用すると、区切り文字パターンを明示的に定義できるため、# や + の文字を単語の一部として扱うことができます。
CREATE TABLE hold_my_beer
(
id UInt64,
description String,
INDEX idx description TYPE text(tokenizer = splitByRegexp('[^\p{L}\p{N}#+]+'))
)
ENGINE = MergeTree ORDER BY id;
INSERT INTO hold_my_beer VALUES
(1, 'I am an expert in C++'),
(2, 'I am an expert in C#'),
(3, 'I use Arch');
SELECT id, description FROM hold_my_beer WHERE hasAllTokens(description, 'C++') OR hasAllTokens(description, 'C#');┌─id─┬─description───────────┐
│ 1 │ I am an expert in C++ │
│ 2 │ I am an expert in C# │
└────┴───────────────────────┘URL データベースエンジン
貢献者: Alexey Milovidov
2026年7月の 26.7 リリースブログ記事 では、url テーブル関数と URL テーブルエンジンが、HTTP に加えてファイルパス、S3、GCS、Azure、HDFS をサポートし、指定された URL スキーマに基づいて適切なバックエンドにディスパッチできるようになったことについて紹介しました。
26.8 では、URL プレフィックスを指定してリモートサーバー上の任意のパスをテーブルとしてクエリできる新しい URL データベースエンジン が導入されました。
2026年7月の記事の例では、url テーブルエンジンを使用して S3 バケットを直接クエリする方法を紹介しました。
SELECT count(), avg(star_rating) FROM url('s3://datasets-documentation/amazon_reviews/amazon_reviews_2015.snappy.parquet');今回のリリースにより、プレフィックスとして s3://datasets-documentation/amazon_reviews を指定し、バケット内の各ファイルを個別のテーブルとしてクエリできるようになりました。
CREATE DATABASE datasets
ENGINE = URL('https://datasets-documentation.s3.eu-west-3.amazonaws.com/amazon_reviews/');
USE datasets;
SELECT count() FROM 'amazon_reviews_2015.snappy.parquet';┌──count()─┐
│ 41905631 │ -- 41.91 million
└──────────┘SELECT count()
FROM 'amazon_reviews_2014.snappy.parquet';┌──count()─┐
│ 44127569 │ -- 44.13 million
└──────────┘clickhouse-local では、デフォルトのデータベースが URL 上のオーバーレイにもなったため、ローカルファイル、URL、S3 データ、またはデフォルトデータベース内の通常のテーブルを便利にクエリできるようになりました。
SELECT * FROM 'hits.tsv';
SELECT * FROM 'https://example.com/hits.tsv';
SELECT * FROM 's3://mybucket/hits.tsv';
SELECT * FROM table;bigquery テーブル関数と BigQuery テーブルエンジン
貢献者: Alexey Milovidov
ワークロードを BigQuery から ClickHouse にまだ移行していない方のために、26.8 では bigquery テーブル関数と BigQuery テーブルエンジンが導入され、移行がこれまで以上に簡単になりました。
Stack Overflow の投稿データを含む既存の BigQuery プロジェクト (セットアップ手順) を使用して、どのように機能するか見てみましょう。
まず、bigquery テーブル関数を使用してデータの構造を確認します。これには、bigquery テーブル関数にサービスキーを渡す必要があります。
名前付きコレクション (Named collection) を作成し、以下の your_service_key を Google Cloud コンソールから取得した .json サービスキーの内容に置き換えます。
<clickhouse>
<named_collections>
<bigquery_credentials>
<project>bigquery-clickhouse</project>
<dataset>stackoverflow</dataset>
<table>badges</table>
<service_account_key><![CDATA[your_service_key]]></service_account_key>
</bigquery_credentials>
</named_collections>
</clickhouse>名前付きコレクションが存在することを確認します。
SELECT *
FROM system.named_collections;┌─name────────┬─collection───────────────────────────────────────────────────────────────────┬─source─┬─create_query─┐
│ my_bigquery │ {'dataset':'[HIDDEN]','project':'[HIDDEN]','service_account_key':'[HIDDEN]'} │ CONFIG │ │
└─────────────┴──────────────────────────────────────────────────────────────────────────────┴────────┴──────────────┘次に、名前付きコレクションを bigquery テーブル関数の引数として使用し、データの構造を確認します。
DESCRIBE TABLE bigquery(bigquery_credentials);Query id: dd1e8285-b286-4892-8770-eeed49ef6919
┌─name─────┬─type───────────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ Id │ Nullable(Int64) │ │ │ │ │ │
│ UserId │ Nullable(Int64) │ │ │ │ │ │
│ Name │ Nullable(String) │ │ │ │ │ │
│ Date │ Nullable(DateTime64(6, 'UTC')) │ │ │ │ │ │
│ Class │ Nullable(Int64) │ │ │ │ │ │
│ TagBased │ Nullable(Int64) │ │ │ │ │ │
└──────────┴────────────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
6 rows in set. Elapsed: 0.602 sec.これで、ローカルに同じテーブルを簡単に作成してデータを挿入できます。
CREATE TABLE bq_badges
(
Id Int64,
UserId Int64,
Name LowCardinality(String),
Date DateTime64(6, 'UTC'),
Class Int64,
TagBased Int64
)
ENGINE = MergeTree
ORDER BY (Id);
INSERT INTO bq_badges SELECT * FROM bigquery(bigquery_credentials) LIMIT 5; -- I don't want to max out my credit card
SELECT * FROM bq_badges;┌───────Id─┬───UserId─┬─Name────────┬───────────────────────Date─┬─Class─┬─TagBased─┐
│ 3336768 │ 1033808 │ Copy Editor │ 2012-05-03 22:19:43.187000 │ 1 │ 0 │
│ 6687355 │ 19299 │ .net │ 2013-06-15 03:03:52.690000 │ 1 │ 1 │
│ 12885730 │ 3892259 │ Copy Editor │ 2015-01-31 14:43:59.373000 │ 1 │ 0 │
│ 39830489 │ 10659482 │ Copy Editor │ 2020-12-09 11:16:11.410000 │ 1 │ 0 │
│ 40413130 │ 1386551 │ Copy Editor │ 2021-01-29 18:00:54.783000 │ 1 │ 0 │
└──────────┴──────────┴─────────────┴────────────────────────────┴───────┴──────────┘
5 rows in set. Elapsed: 0.005 sec.S3 Tables
貢献者: Konstantin Vedernikov
ClickHouse 26.8 では、Iceberg テーブル向けの AWS のフルマネージドサービスである Amazon S3 Tables の書き込みサポートが追加されました。既存のテーブルのクエリに加えて、データの挿入や新しいテーブルの作成ができるようになりました。
まず、Iceberg カタログの統合と書き込みを有効にします。
SET allow_database_iceberg = 1;
SET allow_insert_into_iceberg = 1;テーブルバケットにアクセスするための AWS 認証情報が設定されていれば、DataLakeCatalog データベースエンジンを使用して接続できます。以下のリージョンとウェアハウス ARN を独自のものに置き換えてください。
CREATE DATABASE tables
ENGINE = DataLakeCatalog(
'https://s3tables.us-east-1.amazonaws.com/iceberg'
)
SETTINGS
catalog_type = 's3tables',
region = 'us-east-1',
warehouse = 'arn:aws:s3tables:us-east-1:123456789012:bucket/analytics';これで利用可能なテーブルを一覧表示し、クエリを実行できます。例えば、バケット内の ns ネームスペースに events テーブルが含まれている場合は次のようにします。
SHOW TABLES FROM tables;
SELECT *
FROM tables.`ns.events`
LIMIT 10;テーブルのスキーマに一致する値を指定して、テーブルへの書き込みも行えるようになりました。
-- Replace … with values matching your table's columns.
INSERT INTO tables.`ns.events` VALUES (…);Snowflake Horizon
貢献者: Melvyn Peignon
ClickHouse 26.8 では、Snowflake Horizon カタログを介した Iceberg テーブルの読み取りと書き込みのサポートが追加されました。ClickHouse は、オブジェクトストレージから基盤となるデータファイルを直接読み取ります。
Iceberg カタログの統合と書き込みを有効にし、カタログに接続してみましょう。以下のアカウントエンドポイント、データベース名、個人用アクセストークン (PAT)、Snowflake ロールをご自身のものに置き換えてください。指定するロールには、使用する Iceberg テーブルへのアクセス権が必要です。
SET allow_database_iceberg = 1;
SET allow_insert_into_iceberg = 1;
CREATE DATABASE horizon
ENGINE = DataLakeCatalog(
'https://<org>-<account>.snowflakecomputing.com/polaris/api/catalog'
)
SETTINGS
catalog_type = 'horizon',
warehouse = 'ICEBERG_DB',
catalog_credential = '<PAT>',
auth_scope = 'session:role:<ROLE>',
vended_credentials = 1;これで利用可能なテーブルを一覧表示し、クエリを実行できます。例えば、カタログ内の schema ネームスペースに trades テーブルが含まれている場合は次のようにします。
SHOW TABLES FROM horizon;
SELECT *
FROM horizon.`schema.trades`
LIMIT 10;データを挿入するには、テーブルのスキーマに一致する値を指定します。
-- Replace … with values matching your table's columns.
INSERT INTO horizon.`schema.trades` VALUES (…);ClickHouse は Horizon カタログを通じて変更をコミットします。
Puffin ファイル形式
貢献者: Konstantin Vedernikov
ClickHouse 26.8 では、Apache Iceberg が統計情報や削除ベクトルの保存に使用する Puffin ファイル形式のサポートが追加されました。
ClickHouse テストスイートの小さなサンプルファイルを使用して、これがどのように機能するか見てみましょう。
SELECT referenced_data_file, deleted_rows
FROM url(
'https://raw.githubusercontent.com/ClickHouse/ClickHouse/693dee22dda0d7ff23343c326754ccfc24f3237f/tests/queries/0_stateless/data_puffin/file_properties_ok.puffin',
'Puffin'
);┌─referenced_data_file───────────┬─deleted_rows─┐
│ /data/table/part-00000.parquet │ [2,5] │
└───────────────────────────────┴──────────────┘これは、参照先の Parquet ファイル内の行位置 2 と 5 が削除済みとしてマークされていることを示しています。その Parquet ファイルにアクセスする必要はありません。削除情報は Puffin ファイル自体に保存されています。これにより、データファイルに存在する行が Iceberg テーブルのクエリ時に表示されない理由を把握しやすくなります。
また、PuffinMetadata 形式を使用して blob のタイプとプロパティを調査することもできます。
SELECT blob_type, properties
FROM url(
'https://raw.githubusercontent.com/ClickHouse/ClickHouse/693dee22dda0d7ff23343c326754ccfc24f3237f/tests/queries/0_stateless/data_puffin/file_properties_ok.puffin',
'PuffinMetadata'
);このファイルの場合、blob タイプは deletion-vector-v1 であり、そのプロパティには cardinality として 2 が含まれており、上記の削除された 2 つの行位置と一致しています。
Iceberg におけるマニフェストファイルのプリフェッチ
貢献者: Asya Shneerson、Konstantin Vedernikov
Iceberg テーブルからデータを読み取る前に、ClickHouse はそのデータファイルと削除ファイルを記述したマニフェストファイルを読み取ります。このメタデータの取得と処理は、特にオブジェクトストレージへの多数のリクエストが必要な場合に、無視できない起動時間を追加する可能性があります。
26.8 では、ClickHouse は現在のマニフェストファイルをパースしながら次のマニフェストファイルをプリフェッチし、ストレージの読み取りと CPU 処理をオーバーラップさせます。
また、削除マニフェストも並行して読み取りおよびデコードされます。これらはデータファイルを読み取る前に処理する必要があるため、削除ファイルが多数あるテーブルでのクエリの開始が高速化されます。iceberg_delete_manifest_decode_concurrency 設定は一度にデコードされる削除マニフェストの数を制御し、デフォルトは 4 です。
このリリースでは、データレイクカタログ向けの S3 バケットリージョンキャッシュも修正され、バケットのリージョンを特定するためのリクエストが繰り返されるのを回避できるようになりました。
ネイティブ Parquet の改善
貢献者: Alexey Milovidov、Vasily Chekalkin
ClickHouse 26.8 では、Parquet の読み取りに関して複数の改善が行われ、クエリが不要なデータをスキップできるようになりました。
ORDER BY と LIMIT を含むクエリの場合、ClickHouse はまずソートとフィルタリングに必要なカラムを読み取り、制限を通過した行に対してのみ残りのカラムを取得できます。この最適化はデフォルトで有効になっています。
公開されている Parquet データセットで、同点の場合の順序付けに WatchID を使用して最新のイベントを 10 件検索してみましょう。
SELECT URL, Title
FROM s3(
'https://clickhouse-public-datasets.s3.amazonaws.com/hits_compatible/hits.parquet',
NOSIGN
)
ORDER BY EventTime DESC, WatchID DESC
LIMIT 10
SETTINGS query_plan_optimize_lazy_materialization_for_object_storage = 1;query_plan_optimize_lazy_materialization_for_object_storage を 0 に設定して遅延読み取りを無効にした場合と比較できます。ClickHouse 26.8.2.7 での繰り返しのテストでは、最適化を無効にした場合にクエリは S3 から約 6.6〜6.7 GB を読み取りましたが、有効にした場合は同じ 10 行を返しつつ 1.5 GB の読み取りに抑えられました。
辞書ベースのフィルタリングも、等価条件や IN 条件に対して ClickHouse がデータをスキップするのに役立ちます。特定の電話モデルに一致する行を検索してみましょう。
SELECT count()
FROM s3(
'https://clickhouse-public-datasets.s3.amazonaws.com/hits_compatible/hits.parquet',
NOSIGN
)
WHERE MobilePhoneModel = 'GT-C3262';┌─count()─┐
│ 200 │
└─────────┘カラムチャンクが完全に辞書エンコードされている場合、要求された値が辞書に存在しなければ、ClickHouse はその行グループをスキップできます。これは、最小値/最大値の統計情報で一致を除外できず、Bloom フィルターが利用できない場合でも有効です。
辞書フィルタリングを有効にすると、このクエリが処理したデータページは 12 ページでしたが、無効にした場合は 226 ページでした。どちらのクエリも 200 を返しました。input_format_parquet_dictionary_filter_push_down 設定のデフォルトは 1048576 (1 MiB の辞書ページ制限) であり、0 に設定するとこの最適化が無効になります。
GeoParquet のクエリも空間プルーニングの恩恵を受けます。ClickHouse はバウンディングボックス情報を使用して無関係な行グループやページをスキップし、行の読み取り中に空間述語を適用します。
GROUP BY のパフォーマンス改善
貢献者: Nihal Z. Miaji、Dmitriy Terenichev、Konstantin Bogdanov、Harikrishnan Prabakaran
26.8 リリースでは、以下を含む GROUP BY の一連のパフォーマンス改善も行われました。
- マージアグリゲーターと分割アグリゲーターが使用するアプローチを適応的に組み合わせる、並列
GROUP BY向けの新しいアルゴリズム。 - 26.7 リリースではキーでソートされたテーブルに対して
GROUP BYとORDER BY LIMITの融合が行われましたが、26.8 では任意の読み取り順序でこれを行い、結果に現れ得ないグループをプルーニングします。 - 単一文字列キーを使用する
GROUP BYで、大幅に小さなハッシュテーブルセルが使用されるようになりました。 - 並列
GROUP BYクエリの最終マージステップでマルチスレッドが使用されるようになり、シングルスレッドのボトルネックになるのを防ぎます。
これらの最適化の詳細については、別の記事で詳しく説明します。
Join
貢献者: Han Fei、Anton Popov、Robert Schulze、Alexey Milovidov、Vladimir Cherkasov、Alexander Gololobov
毎回のリリースと同様に、Join に関しても以下のようなさらなる改善が行われました。
- 小さなテーブルに対して、カラムの統計情報がデフォルトで
INSERT時に構築されるようになり、すべての TPC-H ベンチマークで 29% の向上が見られました。 - 条件が 2 つの不等式である JOIN は、従来はフィルタリング付きの
CROSS JOINとして実行されていました。これが、ソートベースのIEJoinアルゴリズムを使用するように変更されます。 - すべてのコアで実行される新しいマージ結合アルゴリズム
parallel_full_sorting_mergeが追加されました。入力はキーのハッシュによってシャードごとの独立したマージ結合にシャーディングされます。 - 分散クエリプラン向けのコストベースのオプティマイザ。新しいコストベースのオプティマイザは、カーディナリティの見積もりを使用して、分散結合、集計、ソート、およびデータ転送の実行方法を選択します。
これらの機能についても、別の記事で詳しく説明する予定です。
今すぐ始める
実際のデータで ClickHouse の動作を確認してみませんか?ClickHouse Cloud は数分で使い始めることができ、300 ドル分の無料クレジットも進呈されます。
サインアップ


