---
title: "ClickHouse リリース 26.10"
date: "2026-11-09T04:36:09.187Z"
author: "ClickHouse"
category: "Engineering"
excerpt: "ClickHouse 26.10 では、関数合成、より柔軟な JOIN、高速化された GROUP BY クエリ、IN サブクエリのディスクスピル、実験的なカラムキャッシュ、Freeform および SQLite フォーマットが導入されました。"
---

# ClickHouse リリース 26.10

またひと月が過ぎ、新たなリリースの時期がやってきました！

<p>ClickHouse 26.10 リリースには、104 件の新機能 &#127875;、225 件のパフォーマンス最適化 &#127810;、739 件のバグ修正 &#129417; が含まれています。</p>

本リリースでは、SQL の可読性を高める関数合成、より柔軟な JOIN 条件、テキストおよび SQLite ファイルを扱う新しい手段が導入されています。

さらに、GROUP BY クエリの高速化、IN サブクエリのディスクスピル、同じデータの解凍やデシリアライズを繰り返すのを防ぐ実験的なカラムキャッシュも追加されました。

## 新規コントリビューター

26.10 の新規コントリビューターの皆さまを心より歓迎します！ClickHouse コミュニティの成長にはいつも身の引き締まる思いであり、ClickHouse の普及を支えてくださる数多くの貢献に深く感謝いたします。

新規コントリビューターの皆さまのお名前は以下のとおりです。

*ARON, Aarav, Abdullah Kaya, Akanshaak, Aleh Strakachuk, Aleksandr Efimov, Alexey Sitnikov, Andrea Debernardi, Andrew Wilkins, Angelica Ismailos, Armin Braun, Arvind Kumar, Ashish Choubey, Azarudeen Shariff A, Azhar Izzannada E, Bentsi Leviav, Chester, Craig Ringer, David Rolfe, Denis Voytyuk, Dimitri König, Frank Denis, Fábio Neves, GON, Gheorghe Pucea, Ilias, Ilias Aberkane, Javier G. Sogo, Jay Pokale, Jaysurya Ray, Jonathan Ackerman, Knut-Aage-Hofseth, Krithika Balagurunathan, LeeTeng2001, LyJacky, Maksim Andreev, Mike Lewis, Mishalski, Mohammed Sohail, Nick Akey, Oleg Milanov, Prince Maurya, RJ, Ray Winkelman, Rion Bora, Sam Wolfberg, Shahzeb Ali, Thomas Sileghem, Tom Alard, Unamed, Uwez Khan, Vadim Lopatin, Vector, Vitaly Pryakhin, Vlad, VrtxOmega, Zach Rippas, ZainnQureshii, ajay tomar, amogiska, antonioscaramuzzino, arc1999, artemkulyk, awss, azharizz, bentsileviav, bisht2050, clickgapai, comino, dhruvtaneja-ch, dosedaf, erezrokah, gonappuccino, jacky, linjiayu1025-collab, namtran1812, seva-potapov, sunliqiang, superdiaodiao, tianhui-fu, tridao, turbolytics, turbolytics.io, ultrabig, vector524*

ヒント: このリストの作成方法に興味がある方は…[こちら](https://gist.github.com/gingerwizard/5a9a87a39ba93b422d8640d811e269e9)をご覧ください。

<iframe width="768" height="432" src="https://www.youtube.com/embed/k30ikPXNpTQ?si=oNEc1DPhJU-Lh3rm" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

[プレゼンテーションのスライド](https://presentations.clickhouse.com/2026-release-26.10)もご覧いただけます。

## 関数合成演算子

### コントリビューター: Ivan Esipov、Alexey Milovidov

[`arrayMap`](https://clickhouse.com/docs/ja/reference/functions/regular-functions/array-functions#arrayMap) や [`arrayFilter`](https://clickhouse.com/docs/ja/reference/functions/regular-functions/array-functions#arrayFilter) といった ClickHouse の高階関数を使ったことがある方なら、渡すラムダ式がすぐに関数呼び出しの多重ネストになってしまうのをご存じでしょう。ClickHouse 26.10 では[関数合成演算子](https://clickhouse.com/docs/ja/reference/operators)が追加され、`|` 演算子で関数をつなぐパイプラインを作成できるようになりました。これにより、ラムダ式の可読性が大幅に向上します。

URL からサイト名を抽出する処理で、2 つのスタイルを比較してみましょう。
URL を小文字化し、`www` プレフィックスを取り除き、最初の主要なサブドメインを取得してから大文字化します。

```sql
WITH [
    'https://WWW.ClickHouse.com/docs',
    'https://GitHub.com/ClickHouse/ClickHouse',
    'https://www.anthropic.com/news'
] AS urls
SELECT
    arrayMap(x -> upper(firstSignificantSubdomain(domainWithoutWWW(lower(x)))), urls) AS with_lambda,
    arrayMap(lower | domainWithoutWWW | firstSignificantSubdomain | upper, urls) AS with_composition
FORMAT Vertical;
```

```text
Row 1:
──────
with_lambda:      ['CLICKHOUSE','GITHUB','ANTHROPIC']
with_composition: ['CLICKHOUSE','GITHUB','ANTHROPIC']
```

どちらのカラムも同じ結果を返しますが、合成演算子を使ったバージョンのほうが一連の手順のように読み取れます。この例の各関数は渡された値だけで呼び出せるため、値の配置場所を指定する必要はありませんでした。

関数に追加の引数を渡す場合は、パイプラインからの値が入る位置を `_` で指定します。たとえば `substring(_, 1, 3)` は、渡された文字列の先頭 3 文字を取得します。

```sql
WITH ['ClickHouse', 'Anthropic', 'OpenAI'] AS companies
SELECT
    arrayMap(x -> substring(upper(x), 1, 3), companies) AS with_lambda,
    arrayMap(upper | substring(_, 1, 3), companies) AS with_composition
FORMAT Vertical;
```

```text
Row 1:
──────
with_lambda:      ['CLI','ANT','OPE']
with_composition: ['CLI','ANT','OPE']
```

高階関数には同じ長さの配列を複数渡すこともでき、位置ごとに並行して処理されます。その場合は、1 つ目の配列の要素に `_1`、2 つ目の配列の対応する要素に `_2` のように指定します。注文の明細合計を計算するために、各価格に数量を掛けて、その結果を価格形式に整形してみましょう。

```sql
WITH
    [49.99, 189.00, 19.50] AS prices,
    [2, 1, 4] AS quantities
SELECT
    arrayMap(multiply(_1, _2) | toString | concat('$', _), prices, quantities) AS line_totals;
```

```text
┌─line_totals─────────────┐
│ ['$99.98','$189','$78'] │
└─────────────────────────┘
```

プレースホルダーは、`|` を使わずに単独でラムダ式の短縮記法としても機能します。たとえば、以下の例に示すように、`_1 * _2` は `(price, quantity) -> price * quantity` と同じ意味になります。

```sql
WITH
    ['Keyboard', 'Monitor', 'Mouse'] AS products,
    [49.99, 189.00, 19.50] AS prices,
    [2, 1, 4] AS quantities
SELECT
    arrayMap(_1 * _2, prices, quantities) AS line_totals,
    arrayMap(concat(_1, ' x', toString(_2)), products, quantities) AS order_lines
FORMAT Vertical;
```

```text
Row 1:
──────
line_totals: [99.98,189,78]
order_lines: ['Keyboard x2','Monitor x1','Mouse x4']
```

内部的には、ClickHouse はクエリ解析中にパイプラインを通常のラムダ式へ変換します。これは、`run_query_tree_passes = 1` を指定して [`EXPLAIN SYNTAX`](https://clickhouse.com/docs/ja/reference/statements/explain#explain-syntax) を実行すると確認できます。アナライザーによる書き換え後のクエリが表示されます。

```sql
EXPLAIN SYNTAX run_query_tree_passes = 1
SELECT arrayMap(
    lower | domainWithoutWWW | firstSignificantSubdomain | upper,
    ['https://WWW.ClickHouse.com/docs']
) AS sites;
```

```text
SELECT arrayMap(lambda(tuple(__composed_arg_1), upper(firstSignificantSubdomain(domainWithoutWWW(lower(__composed_arg_1))))), _CAST(['https://WWW.ClickHouse.com/docs'], 'Array(String)')) AS sites
FROM system.one AS __table1
```

`|` のチェーンは、最初にお見せしたのと同じネストされた式を持つラムダ式へ変換されているため、どちらのスタイルもまったく同一に実行されます。お好みの書き方を選択できます。

## 挿入行数に対するクォータ

### コントリビューター: Alexey Milovidov

[クォータ](https://clickhouse.com/docs/ja/reference/statements/create/quota)を使うと、一定の時間間隔内でユーザーが実行できる処理量を制限できます。ClickHouse 26.10 では `written_rows` カウンターが追加され、ユーザーが書き込む行数によってデータ取り込みを制限できるようになりました。

テーブルと、1 分あたり最大 250 回の挿入、合計最大 10 万行まで書き込める `ingest` ユーザーを作成してみましょう。

```sql
CREATE TABLE events (id UInt64) 
ORDER BY id;
```

```sql
CREATE USER ingest IDENTIFIED WITH no_password;
GRANT INSERT, SELECT ON default.events TO ingest;
```

```sql
CREATE QUOTA ingest_limit
FOR INTERVAL 1 minute MAX written_rows = 100000, query_inserts = 250
TO ingest;
```

最初の 6 万行の挿入は何の問題もなく完了します。

```sql
INSERT INTO events 
SELECT number 
FROM numbers(60000);
```

しかし、同じ 1 分以内にもう一度同じ挿入を実行すると、上限を超えてしまいます。

```text
Received exception:
Code: 201. DB::Exception: Quota for user `ingest` for 60s has been exceeded: written_rows = 120000/100000. Interval will end at 2026-10-23 14:00:00. Name of quota template: `ingest_limit`. (QUOTA_EXCEEDED)
```

拒否された挿入ではデータが書き込まれないため、テーブルには 6 万行が保持されたままになります。他のクォータと同様に、各ユーザーが上限にどれだけ近づいているかは [`system.quotas_usage`](https://clickhouse.com/docs/ja/reference/system-tables/quotas_usage) で確認できます。

```sql
SELECT quota_name, written_rows, max_written_rows, query_inserts, max_query_inserts
FROM system.quotas_usage
WHERE quota_name = 'ingest_limit'
FORMAT Vertical;
```

```text
Row 1:
──────
quota_name:        ingest_limit
written_rows:      120000
max_written_rows:  100000
query_inserts:     2
max_query_inserts: 250
```

## 新しい Array および Map 関数

### コントリビューター: Minh Vu、Jimmy Aguilar Mena、UnamedRus、Alexey Milovidov

この 26.10 リリースでは、配列やマップを操作するための新しい関数もいくつか追加されています。

[`arrayMinIndex`](https://clickhouse.com/docs/ja/reference/functions/regular-functions/array-functions#arrayMinIndex) と [`arrayMaxIndex`](https://clickhouse.com/docs/ja/reference/functions/regular-functions/array-functions#arrayMaxIndex) は、各要素にラムダ式を適用した上で(省略可能)、最小要素および最大要素の位置を返します。[`arrayRemoveAt`](https://clickhouse.com/docs/ja/reference/functions/regular-functions/array-functions) は指定された位置の要素を削除し、負の位置を指定した場合は末尾から逆算します。

いくつかの小さな配列で、それぞれが何を返すか確認してみましょう。

```sql
WITH [2,5,10,9,7] AS values

SELECT
    arrayMinIndex(values) AS min_index,
    arrayMaxIndex(values) AS max_index,
    arrayMaxIndex(x -> length(x), ['a', 'abc', 'ab', 'abcd']) AS longest_index,
    arrayRemoveAt(values, 2) AS remove_second,
    arrayRemoveAt(values, -1) AS remove_last
FORMAT Vertical;
```

```text
Row 1:
──────
min_index:     1
max_index:     3
longest_index: 4
remove_second: [2,10,9,7]
remove_last:   [2,5,10,9]
```

> ClickHouse の配列インデックスは 1 から始まるため、配列の先頭位置にある最小値 `2` のインデックスは `1` になります。

この結果を別の配列にそのまま渡して、対応する値を参照することもできます。以下の例では、1 週間の中で最も寒い日と最も暖かい日を特定し、その後に金曜日をリストから除外しています。

```sql
WITH
    ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'] AS days,
    [18.5, 21.2, 16.9, 24.3, 22.0] AS temps
SELECT
    days[arrayMinIndex(temps)] AS coldest,
    days[arrayMaxIndex(temps)] AS warmest,
    arrayRemoveAt(days, -1) AS without_friday;
```

```text
┌─coldest─┬─warmest─┬─without_friday────────────┐
│ Wed     │ Thu     │ ['Mon','Tue','Wed','Thu'] │
└─────────┴─────────┴───────────────────────────┘
```

マップ向けには、キーを削除する [`mapRemove`](https://clickhouse.com/docs/ja/reference/functions/regular-functions/tuple-map-functions#mapRemove)、キーと値のペアをタプルの配列として返す [`mapEntries`](https://clickhouse.com/docs/ja/reference/functions/regular-functions/tuple-map-functions#mapEntries)、マップに特定のキーと値のペアが含まれているかを判定する [`mapContainsKeyValue`](https://clickhouse.com/docs/ja/reference/functions/regular-functions/tuple-map-functions#mapContainsKeyValue) が追加されました。

```sql
WITH map('env', 'prod', 'region', 'eu-west-1', 'team', 'payments') AS labels
SELECT
    mapRemove(labels, 'team') AS without_team,
    mapEntries(labels) AS entries,
    mapContainsKeyValue(labels, 'env', 'prod') AS is_prod
FORMAT Vertical;
```

```text
Row 1:
──────
without_team: {'env':'prod','region':'eu-west-1'}
entries:      [('env','prod'),('region','eu-west-1'),('team','payments')]
is_prod:      1
```

`mapContainsKeyValue` は[テキストインデックス](https://clickhouse.com/docs/ja/reference/engines/table-engines/mergetree-family/textindexes)も利用できるため、行数が膨大な場合でもラベルによるフィルタリングが格段に高速化されます。

最後に、[`-Map` コンビネーター](https://clickhouse.com/docs/ja/reference/functions/aggregate-functions/combinators#-map)で `Map` カラムが不要になりました。代わりに最後の引数としてキーを渡せるようになり、行から直接キーごとの集約を簡単に行えるようになりました。

```sql
SELECT
    sumMap(views, country) AS views_by_country,
    argMaxMap(page, views, country) AS top_page_by_country
FROM VALUES('country String, page String, views UInt32',
    ('UK', '/docs', 120), ('UK', '/blog', 340),
    ('US', '/docs', 910), ('US', '/pricing', 450),
    ('DE', '/blog', 75));
```

```text
┌─views_by_country─────────────┬─top_page_by_country──────────────────────┐
│ {'DE':75,'UK':460,'US':1360} │ {'DE':'/blog','UK':'/blog','US':'/docs'} │
└──────────────────────────────┴──────────────────────────────────────────┘
```

## SQL 互換の整数除算

### コントリビューター: Ilya Yatsishin

ClickHouse では、整数同士の除算は常に浮動小数点数を返してきたため、`7 / 2` の結果は `3.5` になります。一方、PostgreSQL、SQL Server、SQLite ではいずれも小数点以下が切り捨てられて `3` になります。

リリースコールでの Alexey の見解は「ClickHouse が正しく、他がすべて間違っている」というものでしたが、他のデータベースからクエリを移行する際に同じように切り捨てる必要がある場合に備えて設定が追加されました。デフォルトの挙動は以下のとおりです。

```sql
SELECT 7 / 2, -7 / 2, 7 / 2.0, toTypeName(7 / 2);
```

```text
┌─divide(7, 2)─┬─divide(-7, 2)─┬─divide(7, 2.)─┬─toTypeName(divide(7, 2))─┐
│          3.5 │          -3.5 │           3.5 │ Float64                  │
└──────────────┴───────────────┴───────────────┴──────────────────────────┘
```

PostgreSQL 方式を好む場合は、[`sql_compatible_division_for_integers`](https://clickhouse.com/docs/ja/reference/settings/session-settings/other) を有効化できます。

```sql
SET sql_compatible_division_for_integers = 1;

SELECT 7 / 2, -7 / 2, 7 / 2.0, toTypeName(7 / 2);
```

```text
┌─divide(7, 2)─┬─divide(-7, 2)─┬─divide(7, 2.)─┬─toTypeName(divide(7, 2))─┐
│            3 │            -3 │           3.5 │ UInt8                    │
└──────────────┴───────────────┴───────────────┴──────────────────────────┘
```

結果はゼロ方向に切り捨てられて整数型が維持されますが、浮動小数点数による除算は引き続き浮動小数点数を返します。この設定を有効にすると、ゼロ除算時は `inf` を返すのではなくエラーが発生します。

```sql
SET sql_compatible_division_for_integers = 1;

SELECT 1 / 0;
```

```text
Received exception:
Code: 153. DB::Exception: Division by zero: In scope SELECT divideExact(1, 0) AS `divide(1, 0)`. (ILLEGAL_DIVISION)
```

この設定はデフォルトで無効になっています。ビューなどの保存された定義は作成時の除算挙動を維持するため、有効にしても既存のビューの結果が変わることはありません。

## IN サブクエリのディスクスピル

### コントリビューター: Nihal Z. Miaji

サブクエリで `IN` を使用すると、ClickHouse はそのサブクエリが返した値のセットを構築し、外側のクエリの行をそのセットと照合します。セットが大きいと大量のメモリを消費する可能性があります。ClickHouse 26.10 では、[このセットをディスクへスピル(退避)する](https://clickhouse.com/docs/ja/reference/statements/in)機能が追加され、より小さなメモリ制限でもクエリを完了できるようになりました。

1995 年以降のイングランドおよびウェールズの不動産売買データを含む [英国の不動産価格データセット](https://clickhouse.com/docs/ja/get-started/sample-datasets/uk-price-paid) で試してみましょう。2024 年以前にも売買記録がある住所について、2024 年の売買件数を集計します。建物や(該当する場合は)部屋を特定する郵便番号と 2 つの住所フィールド `addr1`、`addr2` で照合します。これは恒久的な不動産 ID ではなく住所ベースの照合であり、ユニークな物件数ではなく売買件数をカウントしています。

まず、クエリのメモリ制限を 4 GiB に設定し、スピルを無効のまま実行してみます。

```sql
SELECT count() AS repeat_address_sales_2024
FROM uk_price_paid
WHERE date >= '2024-01-01'
  AND date < '2025-01-01'
  AND (postcode1, postcode2, addr1, addr2) IN
  (
      SELECT postcode1, postcode2, addr1, addr2
      FROM uk_price_paid
      WHERE date < '2024-01-01'
  )
SETTINGS max_threads = 2,
         max_memory_usage = '4Gi',
         max_bytes_before_external_set = 0;
```

```text
┌─repeat_address_sales_2024─┐
│                    558458 │
└───────────────────────────┘

1 row in set. Elapsed: 5.777 sec. Processed 60.90 million rows, 966.34 MB (10.54 million rows/s., 167.27 MB/s.)
Peak memory usage: 1.96 GiB.
```

サブクエリは住所のセットを構築するために、過去の 2,930 万件の売買を読み取ります。ClickHouse 26.10 では、このクエリのピークメモリ使用量は 1.96 GiB でした。メモリ制限を 512 MiB に下げるとどうなるか確認してみましょう。

```sql
SELECT count() AS repeat_address_sales_2024
FROM uk_price_paid
WHERE date >= '2024-01-01'
  AND date < '2025-01-01'
  AND (postcode1, postcode2, addr1, addr2) IN
  (
      SELECT postcode1, postcode2, addr1, addr2
      FROM uk_price_paid
      WHERE date < '2024-01-01'
  )
SETTINGS max_threads = 2,
         max_memory_usage = '512Mi',
         max_bytes_before_external_set = 0;
```

```text
Elapsed: 0.978 sec. Processed 7.93 million rows, 122.04 MB (8.10 million rows/s., 124.75 MB/s.)

Received exception:
Code: 241. DB::Exception: Query memory limit exceeded: would use 537.51 MiB (attempt to allocate chunk of 63.96 MiB), maximum: 512.00 MiB: While executing CreatingSetsTransform. (MEMORY_LIMIT_EXCEEDED)
```

ClickHouse はメモリ不足(OOM)になります。ここで [`max_bytes_before_external_set`](https://clickhouse.com/docs/ja/reference/settings/session-settings/max-bytes) を設定してスピルを有効にできます。クエリ全体のメモリ制限を 512 MiB に保ったまま、しきい値を 128 MiB に設定してみます。

```sql
SELECT count() AS repeat_address_sales_2024
FROM uk_price_paid
WHERE date >= '2024-01-01'
  AND date < '2025-01-01'
  AND (postcode1, postcode2, addr1, addr2) IN
  (
      SELECT postcode1, postcode2, addr1, addr2
      FROM uk_price_paid
      WHERE date < '2024-01-01'
  )
SETTINGS max_threads = 2,
         max_memory_usage = '512Mi',
         max_bytes_before_external_set = '128Mi';
```

```
┌─repeat_address_sales_2024─┐
│                    558458 │
└───────────────────────────┘

1 row in set. Elapsed: 3.185 sec. Processed 60.90 million rows, 966.48 MB (19.12 million rows/s., 303.44 MB/s.)
Peak memory usage: 203.11 MiB.
```

今回はピークメモリ使用量が 203 MiB になりました。これはスピルのしきい値である 128 MiB を上回っていますが、この設定は厳密なメモリ制限を課すものではなく、スピルを開始するタイミングを ClickHouse に指示するものです。クエリにはデータの読み取りと処理のためのメモリも必要なため、スピルしきい値は全体の制限である 512 MiB よりも低く設定します。

クエリの実行中にスペースキーを押してプロファイルイベントを確認すると、ClickHouse が 1 つのセットをディスクにスピルし、4 つのパートを書き込んだことがわかります。

```text
SetsSpilledToDisk:          1
ExternalSetWritePart:       4
ExternalSetCompressedBytes: 470342352
```

使用したさまざまな構成のまとめは以下の表のとおりです。


| メモリ制限 | スピル | 結果 | ピークメモリ |
|---|---|---|---|
| 4 GiB | 無効 | 558,458 | 1.96 GiB |
| 512 MiB | 無効 | メモリ制限超過 | — |
| 512 MiB | 128 MiB | 558,458 | 203 MiB |

## LowCardinality カラムによる GROUP BY の高速化

### コントリビューター: artemkulyk、Samuel Krempaský、Alexey Milovidov

ClickHouse 26.10 では、複数の [`LowCardinality`](https://clickhouse.com/docs/ja/reference/data-types/lowcardinality) カラムによるグループ化が改善されました。辞書から直接グループ化キーをシリアライズし、キーの重複する組み合わせに対する集約状態のルックアップをキャッシュできるようになりました。

先ほどの `uk_price_paid` テーブルを再利用して、郡(county)、地区(district)、町(town)、郵便番号外側コード(outward postcode)の組み合わせごとに売買件数をカウントし、平均価格を計算してみましょう。4 つのグループ化カラムはすべて `LowCardinality(String)` 型です。

```sql
SELECT
    county,
    district,
    town,
    postcode1,
    count() AS sales,
    avg(price) AS average_price
FROM uk_price_paid
GROUP BY county, district, town, postcode1
SETTINGS use_query_cache = 0
FORMAT Null;
```

ターミナル出力が比較に影響しないよう、[`FORMAT Null`](https://clickhouse.com/docs/ja/reference/formats/Null) を使用して 15,409 行の結果を出力せずにクエリを実行します。

ClickHouse 26.9 でクエリを 3 回実行してみます。

```text
0 rows in set. Elapsed: 0.289 sec. Processed 30.45 million rows, 331.39 MB (105.20 million rows/s., 1.14 GB/s.)
Peak memory usage: 170.67 MiB.

0 rows in set. Elapsed: 0.269 sec. Processed 30.45 million rows, 331.39 MB (113.29 million rows/s., 1.23 GB/s.)
Peak memory usage: 171.81 MiB.

0 rows in set. Elapsed: 0.255 sec. Processed 30.45 million rows, 331.39 MB (119.19 million rows/s., 1.30 GB/s.)
Peak memory usage: 177.98 MiB.
```

次に、同じデータとテーブルレイアウトを使用して ClickHouse 26.10 で 3 回実行します。どちらのバージョンでも、手元のマシンでデフォルトの 12 スレッドを使用しています。

```text
0 rows in set. Elapsed: 0.242 sec. Processed 30.45 million rows, 331.39 MB (125.90 million rows/s., 1.37 GB/s.)
Peak memory usage: 128.49 MiB.

0 rows in set. Elapsed: 0.181 sec. Processed 30.45 million rows, 331.39 MB (168.49 million rows/s., 1.83 GB/s.)
Peak memory usage: 124.16 MiB.

0 rows in set. Elapsed: 0.171 sec. Processed 30.45 million rows, 331.39 MB (178.37 million rows/s., 1.94 GB/s.)
Peak memory usage: 127.35 MiB.
```

各バージョンの最速タイムを比較すると、26.9 では 255 ミリ秒、26.10 では 171 ミリ秒となり、26.10 のほうが 30% 強高速化されています。

## GROUP BY 向けの Top-K 動的フィルタリング

### コントリビューター: Alexey Milovidov、Konstantin Bogdanov

[ClickHouse 25.12](https://clickhouse.com/blog/clickhouse-release-25-12) では、現在の Top-K 境界を利用して不要なカラム読み取りを回避し、minmax インデックスによってグラニュールをスキップする `ORDER BY … LIMIT` クエリ向けの動的フィルタリングが導入されました。ClickHouse 26.10 では、このアプローチが `GROUP BY` クエリにも拡張され、[プライマリインデックス](https://clickhouse.com/docs/ja/concepts/core-concepts/primary-indexes)によるグラニュールスキップが追加されました。

英国の不動産データを再利用して、売買が記録された直近 10 日間を、売買件数および平均価格とともに取得してみましょう。クエリで使用する 2 つのカラムを含み、`date` でソートされた別のテーブルを作成します。このレイアウトにより、ClickHouse は両方のメカニズムを活用できます。価格を読み取る前に過去の売買をフィルタリングし、日付がすべてカットオフ値より古いグラニュールをプライマリインデックスでスキップします。

```sql
CREATE TABLE uk_price_paid_by_date
ENGINE = MergeTree
ORDER BY date
AS SELECT date, price FROM uk_price_paid;

OPTIMIZE TABLE uk_price_paid_by_date FINAL;
```

これにより、3,000 万件の売買データがすべてコピーされ、新しいテーブルが単一のパートにマージされます。出力結果を確認するために ClickHouse 26.10 でクエリを実行します。

```sql
SELECT
    date,
    count() AS sales,
    round(avg(price)) AS average_price
FROM uk_price_paid_by_date
GROUP BY date
ORDER BY date DESC
LIMIT 10
SETTINGS
    enable_group_by_top_k_dynamic_filtering = 0,
    use_query_condition_cache = 0,
    use_query_cache = 0;
```

クエリは次の 10 件の日付を返します。

```text
┌───────date─┬─sales─┬─average_price─┐
│ 2025-07-31 │    26 │        300021 │
│ 2025-07-30 │    39 │        316864 │
│ 2025-07-29 │    92 │        344986 │
│ 2025-07-28 │   220 │        317645 │
│ 2025-07-27 │     4 │        242998 │
│ 2025-07-25 │   926 │        316243 │
│ 2025-07-24 │   388 │        358814 │
│ 2025-07-23 │   437 │        347647 │
│ 2025-07-22 │   373 │        332035 │
│ 2025-07-21 │   662 │        337260 │
└────────────┴───────┴───────────────┘
```

clickhouse-local を使用して、最適化の有無によるテストを実施します。処理時間は CLI の `--time` の出力、読み取り行数とピークメモリは `--print-profile-events` の出力に基づいています。

| 実行回 | クエリ実行時間 | 読み取り行数 | 処理データ量 | ピークメモリ |
|---|---|---|---|---|
| 1 | 45 ms | 30,452,463 | 182.71 MB | 7.18 MiB |
| 2 | 27 ms | 30,452,463 | 182.71 MB | 7.26 MiB |
| 3 | 31 ms | 30,452,463 | 182.71 MB | 7.26 MiB |

[`enable_group_by_top_k_dynamic_filtering`](https://clickhouse.com/docs/ja/reference/settings/session-settings/enable) を `1` に変更すると、以下のようになります。

| 実行回 | クエリ実行時間 | 読み取り行数 | 処理データ量 | ピークメモリ |
|---|---|---|---|---|
| 1 | 9 ms | 1,728,263 | 10.37 MB | 7.30 MiB |
| 2 | 8 ms | 1,793,672 | 10.50 MB | 7.30 MiB |
| 3 | 10 ms | 1,728,263 | 10.11 MB | 7.24 MiB |

結果の表から、フィルタリングを有効にすると ClickHouse の読み取り行数が約 94% 削減されることがわかります。最適化を有効にした実行では、それぞれ 3,529、3,521、3,529 個のグラニュールがスキップされましたが、無効な場合はまったくスキップされませんでした。並列集約ストリームがカットオフ値を検出して公開するタイミングによって、実際の処理量には多少のばらつきが生じます。

グラニュール全体がスキップされたことは、`TopKGranulesSkippedByPrimaryKey` プロファイルイベントで確認できます。`--print-profile-events --profile-events-delay-ms=-1` を指定すると、CLI は最終的なイベント合計を出力します。有効時の最初の実行における関連する値は以下のとおりです。

```text
SelectedRows:                     1728263
SelectedBytes:                    10369578
TopKGranulesSkippedByPrimaryKey:  3529
```

## カラムキャッシュ

### コントリビューター: Alexey Milovidov

ClickHouse 26.10 では実験的なカラムキャッシュが導入され、デシリアライズ済みのカラムデータをメモリ内に保持することで、以降のクエリで同じデータを再度解凍およびデシリアライズする手間を省けます。

英国の不動産データセットを再利用して、最初の住所フィールドに `HOUSE` が含まれる売買件数をカウントしてみましょう。元のソート順を維持したまま、価格と住所のカラムを含むコピーを作成します。

```sql
CREATE TABLE uk_price_paid_columns_cache
ENGINE = MergeTree
ORDER BY (postcode1, postcode2, addr1, addr2)
SETTINGS min_bytes_for_wide_part = 0,
         min_rows_for_wide_part = 0
AS SELECT postcode1, postcode2, addr1, addr2, price FROM uk_price_paid;
```

このキャッシュは現在、[ワイドな MergeTree パート](https://clickhouse.com/docs/ja/reference/engines/table-engines/mergetree-family/mergetree)をサポートしています。両方のサイズしきい値を `0` に設定することで、この形式を強制します。

まず、カラムキャッシュを無効にした状態でクエリを実行してみましょう。

```sql
SELECT count() AS house_sales
FROM uk_price_paid_columns_cache
WHERE addr1 LIKE '%HOUSE%'
SETTINGS use_columns_cache = 0,
         use_query_cache = 0,
         use_query_condition_cache = 0,
         log_queries = 1,
         log_profile_events = 1,
         log_comment = 'sorted-columns-cache:off-1';
```

```text
┌─house_sales─┐
│      904871 │
└─────────────┘
```

ファイルシステムキャッシュをウォームアップするために測定外で 1 回実行した後、ログコメントに `sorted-columns-cache:off-1`、`sorted-columns-cache:off-2`、`sorted-columns-cache:off-3` を指定してこのクエリを 3 回実行します。

続いて [`use_columns_cache`](https://clickhouse.com/docs/ja/reference/settings/session-settings/use#use_columns_cache) を `1` に変更し、コメントに `sorted-columns-cache:on-1`、`sorted-columns-cache:on-2`、`sorted-columns-cache:on-3` を指定してさらに 3 回実行します。有効時の最初の実行でカラムキャッシュにデータが読み込まれ、その後の 2 回でキャッシュが再利用されます。

キャッシュを有効にすると、クエリは `addr1` をキャッシュします。たとえば `price` は読み取られないためキャッシュされません。ただし、`price` に条件を追加した場合は、そのカラムから読み取られたデータもキャッシュされます。

クエリログを有効にしていれば、以下のクエリを使用して [`system.query_log`](https://clickhouse.com/docs/ja/reference/system-tables/query_log) から実行時間とキャッシュカウンターを取得できます。

```sql
SYSTEM FLUSH LOGS;

SELECT
    log_comment AS run,
    query_duration_ms AS queryTime,
    ProfileEvents['ColumnsCacheHits'] AS cacheHits,
    ProfileEvents['ColumnsCacheMisses'] AS cacheMisses,
    ProfileEvents['SelectedRows'] AS selectedRows
FROM
(
    SELECT *
    FROM system.query_log
    WHERE type = 'QueryFinish'
      AND startsWith(log_comment, 'sorted-columns-cache:')
      AND log_comment != 'sorted-columns-cache:warmup'
    ORDER BY query_start_time_microseconds DESC
    LIMIT 6
)
ORDER BY query_start_time_microseconds
FORMAT Markdown;
```

| run | queryTime | cacheHits | cacheMisses | selectedRows |
|:-|-:|-:|-:|-:|
| sorted-columns-cache:off-1 | 34 | 0 | 0 | 30452463 |
| sorted-columns-cache:off-2 | 32 | 0 | 0 | 30452463 |
| sorted-columns-cache:off-3 | 33 | 0 | 0 | 30452463 |
| sorted-columns-cache:on-1 | 37 | 0 | 3718 | 30452463 |
| sorted-columns-cache:on-2 | 12 | 3718 | 0 | 30452463 |
| sorted-columns-cache:on-3 | 12 | 3718 | 0 | 30452463 |

カラムキャッシュが無効の場合、クエリには 32〜34 ミリ秒かかります。ClickHouse がキャッシュを参照しないため、`cacheHits` は 0 です。

キャッシュにデータが取り込まれる実行には 37 ミリ秒かかり、キャッシュを利用するその後の 2 回の実行はそれぞれ 12 ミリ秒で完了し、`addr1` の全 3,718 グラニュールがカラムキャッシュから読み取られました。

`SelectedRows` は変わりません。ClickHouse は依然として 3,045 万行すべてに対してフィルターを評価しますが、デコード済みの住所データを再利用できます。キャッシュはクエリ結果ではなくカラムデータを保存するため、同じデータを読み取る他のクエリもその恩恵を受けられます。

## LATERAL JOIN

### コントリビューター: Dimitri König、Alexey Milovidov

ClickHouse 26.10 では [`LATERAL JOIN`](https://clickhouse.com/docs/ja/reference/statements/select/join#lateral-join) の実験的サポートが追加され、サブクエリが先行するテーブルのカラムを参照できるようになりました。`allow_experimental_lateral_join = 1` を設定することで有効化できます。

[結合のサンプルで使用している IMDb データセット](https://clickhouse.com/ja/blog/clickhouse-fully-supports-joins-part1-jp)を使って、3 人の俳優それぞれについて評価の高い映画上位 3 本を検索してみましょう。`actors` テーブルには俳優の名前、`movies` には映画の詳細と評価、`roles` には俳優が出演した映画との関連付けが格納されています。

```sql
SELECT
    concat(a.first_name, ' ', a.last_name) AS actor,
    top_titles.name AS title,
    top_titles.year AS year,
    round(top_titles.rank, 1) AS rating
FROM actors AS a
JOIN LATERAL
(
    SELECT m.id, m.name, m.year, m.rank
    FROM roles AS r
    INNER JOIN movies AS m ON r.movie_id = m.id
    WHERE r.actor_id = a.id
      AND m.rank IS NOT NULL
    GROUP BY m.id, m.name, m.year, m.rank
    ORDER BY m.rank DESC, m.year DESC, m.id
    LIMIT 3
) AS top_titles ON true
WHERE (a.first_name, a.last_name) IN
    (('Tom', 'Hanks'), ('Meryl', 'Streep'), ('Denzel', 'Washington'))
ORDER BY actor, rating DESC, year DESC, top_titles.id
SETTINGS allow_experimental_lateral_join = 1;
```

12 行目が重要な部分です: `WHERE r.actor_id = a.id`。`a.id` の値は、外側のクエリの `actors AS a` から渡されます。`LATERAL` によって内側のクエリがその値を使用できるようになり、この俳優のトップ 3 作品を検索できるようになります。`LIMIT 3` はデータセット全体から 3 作品を選択するのではなく、俳優ごとに適用されます。

選択された各俳優について、内側のクエリはその役割を検索し、映画の詳細と結合して、評価のない映画を除外します。`GROUP BY` は、1 人の俳優が複数の役柄レコードを持つ場合の映画の重複を排除します。その後、評価順にソートし(同点の場合は公開年と映画 ID で順位を決定)、上位 3 件を返します。

```text
┌─actor─────────────┬─title──────────────────┬─year─┬─rating─┐
│ Denzel Washington │ Glory                  │ 1989 │    8.1 │
│ Denzel Washington │ Much Ado About Nothing │ 1993 │    7.5 │
│ Denzel Washington │ Antwone Fisher         │ 2002 │    7.4 │
│ Meryl Streep      │ Manhattan              │ 1979 │    8.2 │
│ Meryl Streep      │ Deer Hunter, The       │ 1978 │    8.1 │
│ Meryl Streep      │ Adaptation.            │ 2002 │    7.9 │
│ Tom Hanks         │ Saving Private Ryan    │ 1998 │    8.3 │
│ Tom Hanks         │ Green Mile, The        │ 1999 │    8.1 │
│ Tom Hanks         │ Toy Story 2            │ 1999 │    8.1 │
└───────────────────┴────────────────────────┴──────┴────────┘
```

各俳優につき 3 作品ずつ、計 9 行が取得されます。

## 任意の条件による JOIN

### コントリビューター: Vladimir Cherkasov、Alexey Milovidov

ClickHouse 26.10 では、テーブル間に等値条件がないものを含め、[任意の `ON` 条件による JOIN](https://clickhouse.com/docs/ja/reference/statements/select/join#join-with-an-arbitrary-on-condition) もサポートされました。これを使って、URL パターンに基づき Web サイトのトラフィックを分類してみましょう。

まず、ページビュー数を含むテーブルを作成します。

```sql
CREATE TABLE page_views
(
    path String,
    views UInt64
);

INSERT INTO page_views VALUES
    ('/docs/getting-started', 1200),
    ('/docs/sql-reference/functions', 850),
    ('/blog/clickhouse-release-26-10', 2400),
    ('/pricing', 600),
    ('/about', 300);
```

分類ルールは別のテーブルに保存します。ワイルドカード `%` は任意の文字列にマッチします。

```sql
CREATE TABLE page_categories
(
    pattern String,
    category String
);

INSERT INTO page_categories VALUES
    ('/docs/%', 'Documentation'),
    ('/blog/%', 'Blog'),
    ('/pricing', 'Pricing');
```

これで、[`LIKE`](https://clickhouse.com/docs/ja/reference/functions/regular-functions/string-search-functions#like) を使って各パスをカテゴリにマッチさせ、テーブル同士を結合できます。

```sql
SELECT p.path, p.views, c.category
FROM page_views AS p
LEFT JOIN page_categories AS c ON p.path LIKE c.pattern
ORDER BY p.views DESC;
```

```text
┌─path───────────────────────────┬─views─┬─category──────┐
│ /blog/clickhouse-release-26-10 │  2400 │ Blog          │
│ /docs/getting-started          │  1200 │ Documentation │
│ /docs/sql-reference/functions  │   850 │ Documentation │
│ /pricing                       │   600 │ Pricing       │
│ /about                         │   300 │               │
└────────────────────────────────┴───────┴───────────────┘
```

任意の結合による柔軟性にはコストが伴う点にご注意ください。ブロックネストループ結合はすべての行のペアをチェックするため、クロス結合並みに遅くなる可能性があり、クエリプランナーでは最後の手段として使用されます。

## Freeform 入力フォーマット

### コントリビューター: Tri Dao、Alexey Milovidov

ClickHouse 26.10 では、一貫した構造を持つテキストからカラムとその型を推論する `Freeform` 入力フォーマットも追加されました。

`ls -l` の出力を [clickhouse-local](https://clickhouse.com/docs/ja/concepts/features/tools-and-utilities/clickhouse-local) にパイプして、どのように動作するか見てみましょう。パイプされた入力は `table` として利用可能になり、他のテーブルと同様にクエリを実行できます。次のクエリは `ls -l` の出力を記述します:

```bash
ls -l **/*.parquet | 
./clickhouse --input-format Freeform --query "DESCRIBE TABLE table"
```

```text
c0	String
c1	Int64
c2	String
c3	String
c4	Int64
c5	Int64
c6	String
c7	Int64
c8	Int64
c9	String
```

この出力から、`ls -l` が 10 個のカラムを出力しており、それらに `c0` から `c9` という名前が付けられていることがわかります。次のクエリを使って `table` をクエリし、すべてのカラムを返すことができます。

```bash
ls -l **/*.parquet | 
./clickhouse --input-format Freeform --query "FROM table FORMAT Vertical"
```

```text
Row 1:
──────
c0: -rw-r--r--@
c1: 1
c2: markhneedham
c3: staff
c4: 2048074919 -- 2.05 billion
c5: 30
c6: Oct
c7: 16
c8: 23
c9: benchmarks/amazon-columns-cache/data/amazon_reviews_2010.snappy.parquet

Row 2:
──────
c0: -rw-r--r--@
c1: 1
c2: markhneedham
c3: staff
c4: 3013406813 -- 3.01 billion
c5: 30
c6: Oct
c7: 16
c8: 27
c9: benchmarks/amazon-columns-cache/data/amazon_reviews_2011.snappy.parquet
```

カラムにわかりやすい名前を付け、ファイルサイズをフォーマットし、日付フィールドをタイムスタンプに結合してみましょう。この一覧には年が含まれていないため、これらの最近のファイルには現在の年を使用します:

```bash
ls -l **/*.parquet |
./clickhouse --input-format Freeform --query "
    SELECT
        c9 AS file,
        formatReadableSize(c4) AS size,
        parseDateTimeBestEffort(
            format('{} {} {} {}:{}', c5, c6, toYear(today()), c7, c8)
        ) AS modified
    FROM table
    ORDER BY c4 DESC
    FORMAT Vertical"
```

```text
Row 1:
──────
file:     benchmarks/amazon-columns-cache/data/amazon_reviews_2011.snappy.parquet
size:     2.81 GiB
modified: 2026-10-30 16:27:00

Row 2:
──────
file:     benchmarks/amazon-columns-cache/data/amazon_reviews_2010.snappy.parquet
size:     1.91 GiB
modified: 2026-10-30 16:23:00
```

## SQLite のインポートとエクスポート

### コントリビューター: Yue Ni、Alexey Milovidov

ClickHouse はすでに、[データベースエンジン](https://clickhouse.com/docs/ja/reference/engines/database-engines/sqlite), [テーブルエンジン](https://clickhouse.com/docs/ja/reference/engines/table-engines/integrations/sqlite), [テーブル関数](https://clickhouse.com/docs/ja/reference/functions/table-functions/sqlite)を介した SQLite データベースへのクエリ実行をサポートしています。バージョン 26.10 では[入力および出力フォーマットとして SQLite](https://clickhouse.com/docs/ja/reference/formats/SQLite) が追加され、[クエリ結果を SQLite データベースファイルに直接エクスポート](https://clickhouse.com/docs/ja/reference/statements/select/into-outfile)できるようになりました。これにより、SQLite ツールですぐに開いたり、他の人と共有したりできます。

IMDb の `movies` テーブルを再利用して、1990 年代の各年の映画数をカウントし、平均評価を計算してみましょう:

```sql
SELECT
    year,
    toInt64(count()) AS films,
    round(avg(rank), 2) AS average_rating
FROM movies
WHERE year BETWEEN 1990 AND 1999
GROUP BY year
ORDER BY year
INTO OUTFILE 'films.sqlite'
SETTINGS output_format_sqlite_table_name = 'films_by_year';
```

出力ファイルの拡張子が `.sqlite` であるため、ClickHouse は SQLite フォーマットを使用しますが、ファイル名の後に `FORMAT SQLite` を指定して明示的に出力を指定することもできます。`films_by_year` というテーブルに 10 件の結果を書き込みます。

`sqlite3` コマンドラインツールでファイルを開き、クエリを実行できます:

```bash
sqlite3 -header -column films.sqlite \
  'SELECT * FROM films_by_year ORDER BY year LIMIT 5;'
```

```text
year  films  average_rating
----  -----  --------------
1990  6098   5.33
1991  6068   5.69
1992  6580   5.53
1993  6900   5.55
1994  7502   5.43
```

ClickHouse はカラム名と型を推論して、ファイルを読み戻すこともできます:

```sql
SELECT *
FROM file('films.sqlite')
ORDER BY year
LIMIT 5;
```

```text
┌─year─┬─films─┬─average_rating─┐
│ 1990 │  6098 │           5.33 │
│ 1991 │  6068 │           5.69 │
│ 1992 │  6580 │           5.53 │
│ 1993 │  6900 │           5.55 │
│ 1994 │  7502 │           5.43 │
└──────┴───────┴────────────────┘
```

## スキャン中の文字列フィルタリング

### コントリビューター: Alexey Milovidov

ClickHouse 26.10 では、カラムのデシリアライズ中に `LIKE`、`startsWith`、`endsWith` などの文字列フィルターをチェックできるようになり、一致しない文字列のメモリへのコピーを回避できます。この最適化は、`apply_string_filters_during_scan` によってデフォルトで有効になっています。

[Amazon レビューデータセット](https://clickhouse.com/ja/blog/clickhouse-gets-lazier-and-faster-introducing-lazy-materialization-jp)で試してみましょう。Freeform の例にある 2010 年と 2011 年の Parquet ファイルを MergeTree テーブルにロードすると、998 万件のレビューが得られます:

```sql
CREATE TABLE amazon_reviews
ENGINE = MergeTree
ORDER BY tuple()
AS SELECT *
FROM file('benchmarks/amazon-columns-cache/data/amazon_reviews_{2010,2011}.snappy.parquet');
```

`disappointed` という単語を含むレビューをカウントしてみます。まず、カラムキャッシュ、クエリキャッシュ、クエリ条件キャッシュとともに、この最適化を無効化します:

```sql
SELECT count() AS matching_reviews
FROM amazon_reviews
WHERE review_body LIKE '%disappointed%'
SETTINGS apply_string_filters_during_scan = 0,
         use_columns_cache = 0,
         use_query_cache = 0,
         use_query_condition_cache = 0;
```

```text
┌─matching_reviews─┐
│           293115 │
└──────────────────┘
```

このクエリを、テーブルを 1 つの Wide パートにマージした後、デフォルトの 12 スレッドを設定した ClickHouse 26.10 で実行しました。各設定で 1 回ウォームアップを行った後、最適化を無効にした状態と有効にした状態で測定を 3 回ずつ交互に実施しました。以下の所要時間とピークメモリの数値は `system.query_log` によるものです。

`apply_string_filters_during_scan = 0` の場合:

| 実行回 | クエリ時間 | ピークメモリ |
|---|---|---|
| 1 | 1.204 s | 128.66 MiB |
| 2 | 1.303 s | 127.93 MiB |
| 3 | 1.251 s | 129.05 MiB |

`apply_string_filters_during_scan` を `1` に変更した場合:

| 実行回 | クエリ時間 | ピークメモリ |
|---|---|---|
| 1 | 1.091 s | 2.54 MiB |
| 2 | 1.333 s | 2.54 MiB |
| 3 | 1.232 s | 2.54 MiB |

どの実行でも同じ 293,115 件の一致が返されます。実行時間はほぼ同じですが、ピークメモリは約 129 MiB から 2.54 MiB へと約 98% 削減されています。

---

## 今すぐ始める

ご自身のデータで ClickHouse の動作を確認してみませんか？ClickHouse Cloud は数分で使い始めることができ、300 ドル分の無料クレジットも進呈されます。

[サインアップ](https://console.clickhouse.cloud/signUp?loc=blog-cta-2945&utm_blogctaid=2945)

---