> ## Documentation Index
> Fetch the complete documentation index at: https://clickhouse.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> ClickHouse クエリアナライザの詳細ページ

# アナライザ

ClickHouse バージョン `24.3` では、アナライザがデフォルトで有効化されました。
その動作の詳細については、[こちら](/docs/ja/guides/clickhouse/performance-and-monitoring/understanding-query-execution-with-the-analyzer#analyzer)をご覧ください。

<div id="known-incompatibilities">
  ## 既知の非互換性
</div>

多数のバグ修正と新たな最適化の導入に伴い、ClickHouse の動作には互換性に影響する変更もいくつか含まれています。アナライザに対応するようクエリをどのように書き換える必要があるかを判断するために、以下の変更点を確認してください。

<div id="invalid-queries-are-no-longer-optimized">
  ### 無効なクエリは最適化されなくなりました
</div>

従来のクエリプランニング基盤では、クエリの検証ステップの前に AST レベルの最適化が適用されていました。
その結果、元のクエリが有効で実行可能な形に書き換えられることがありました。

アナライザでは、クエリの検証は最適化ステップより前に行われます。
つまり、以前は実行できていた無効なクエリは、現在ではサポートされません。
そのような場合は、クエリを手動で修正する必要があります。

<div id="example-1">
  #### 例 1
</div>

次のクエリでは、集約後に利用できるのは `toString(number)` のみであるにもかかわらず、PROJECTIONリストでカラム `number` を使用しています。
古いアナライザでは、`GROUP BY toString(number)` は `GROUP BY number,` に最適化されることで、このクエリは有効とみなされていました。

```sql theme={null}
SELECT number
FROM numbers(1)
GROUP BY toString(number)
```

<div id="example-2">
  #### 例 2
</div>

同じ問題はこのクエリでも発生します。カラム `number` は、別のキーで集約した後に使用されています。
以前のクエリアナライザは、`number > 5` のフィルタを `HAVING` 句から `WHERE` 句へ移動することで、このクエリを修正していました。

```sql theme={null}
SELECT
    number % 2 AS n,
    sum(number)
FROM numbers(10)
GROUP BY n
HAVING number > 5
```

クエリを修正するには、標準的な SQL 構文に従い、集計されていないカラムに対するすべての条件を `WHERE` 句に移動する必要があります。

```sql theme={null}
SELECT
    number % 2 AS n,
    sum(number)
FROM numbers(10)
WHERE number > 5
GROUP BY n
```

移行を支援するために、アナライザは、集約されていない AND 条件について、以前の `HAVING` 句から `WHERE` 句への 書き換え を再現できます。この動作を有効にするには、`analyzer_compatibility_allow_non_aggregate_in_having = 1` を設定してください。この設定は ClickHouse `26.7` 以降で利用できます。この設定は、`WITH CUBE`、`WITH ROLLUP`、`WITH TOTALS`、および `GROUPING SETS` では無視されます。集約、`grouping`、または非決定論的関数を含む条件は `HAVING` に残ります。いずれかの条件にウィンドウ関数または状態を持つ関数 (たとえば `rowNumberInBlock`) が含まれている場合は、従来の legacy の動作に合わせて、`HAVING` 全体に対する 書き換え が無効になります。

<div id="create-view-with-invalid-query">
  ### 無効なクエリを含む `CREATE VIEW`
</div>

アナライザは常に型チェックを行います。
以前は、無効な `SELECT` クエリを含む `VIEW` を作成できました。
その場合、最初の `SELECT` または `INSERT` の実行時に失敗していました (`MATERIALIZED VIEW` の場合) 。

このような方法で `VIEW` を作成することは、現在ではできません。

<div id="example-view">
  #### 例
</div>

```sql theme={null}
CREATE TABLE source (data String)
ENGINE=MergeTree
ORDER BY tuple();

CREATE VIEW some_view
AS SELECT JSONExtract(data, 'test', 'DateTime64(3)')
FROM source;
```

<div id="known-incompatibilities-of-the-join-clause">
  ### `JOIN`句の既知の非互換性
</div>

<div id="join-using-column-from-projection">
  #### PROJECTIONのカラムを使った `JOIN`
</div>

`SELECT` リストのエイリアスは、デフォルトでは `JOIN USING` のキーとして使用できません。

新しい設定 `analyzer_compatibility_join_using_top_level_identifier` を有効にすると、`JOIN USING` の動作が変わり、左側のテーブルのカラムを直接使う代わりに、`SELECT` クエリのPROJECTIONリスト内の式に基づいて識別子を優先的に解決するようになります。

例えば:

```sql theme={null}
SELECT a + 1 AS b, t2.s
FROM VALUES('a UInt64, b UInt64', (1, 1)) AS t1
JOIN VALUES('b UInt64, s String', (1, 'one'), (2, 'two')) t2
USING (b);
```

`analyzer_compatibility_join_using_top_level_identifier` を `true` に設定すると、以前のバージョンと同様に、結合条件は `t1.a + 1 = t2.b` と解釈されます。
結果は `2, 'two'` になります。
この設定が `false` の場合、結合条件はデフォルトで `t1.b = t2.b` となり、クエリは `2, 'one'` を返します。
`t1` に `b` が存在しない場合、クエリはエラーで失敗します。

<div id="changes-in-behavior-with-join-using-and-aliasmaterialized-columns">
  #### `JOIN USING` と `ALIAS`/`MATERIALIZED` カラムに関する動作の変更
</div>

アナライザでは、`ALIAS` または `MATERIALIZED` カラムを含む `JOIN USING` クエリで `*` を使用すると、デフォルトでそれらのカラムも結果セットに含まれます。

たとえば:

```sql theme={null}
CREATE TABLE t1 (id UInt64, payload ALIAS sipHash64(id)) ENGINE = MergeTree ORDER BY id;
INSERT INTO t1 VALUES (1), (2);

CREATE TABLE t2 (id UInt64, payload ALIAS sipHash64(id)) ENGINE = MergeTree ORDER BY id;
INSERT INTO t2 VALUES (2), (3);

SELECT * FROM t1
FULL JOIN t2 USING (payload);
```

アナライザでは、このクエリの結果に、両方のテーブルの `id` とともに `payload` カラムが含まれます。
一方、以前のアナライザでは、特定の設定 (`asterisk_include_alias_columns` または `asterisk_include_materialized_columns`) が有効になっている場合にのみ、これらの `ALIAS` カラムが含まれ、
カラムの順序も異なる場合がありました。

一貫性があり期待どおりの結果を得るため、特に古いクエリをアナライザに移行する際は、`*` を使うのではなく、`SELECT` 句でカラムを明示的に指定することを推奨します。

<div id="handling-of-type-modifiers-for-columns-in-using-clause">
  #### `USING` 句におけるカラムの型修飾子の扱い
</div>

アナライザでは、`USING` 句で指定されたカラムの共通スーパータイプを決定するルールが標準化され、より予測可能な結果が得られるようになりました。特に、`LowCardinality` や `Nullable` のような型修飾子を扱う場合にその傾向が顕著です。

* `LowCardinality(T)` と `T`: 型 `LowCardinality(T)` のカラムを型 `T` のカラムと JOIN した場合、結果の共通スーパータイプは `T` となり、`LowCardinality` 修飾子は実質的に破棄されます。
* `Nullable(T)` と `T`: 型 `Nullable(T)` のカラムを型 `T` のカラムと JOIN した場合、結果の共通スーパータイプは `Nullable(T)` となり、Nullable の性質が保持されます。

例:

```sql theme={null}
SELECT id, toTypeName(id)
FROM VALUES('id LowCardinality(String)', ('a')) AS t1
FULL OUTER JOIN VALUES('id String', ('b')) AS t2
USING (id);
```

このクエリでは、`id` の共通スーパータイプは `String` と判定され、`t1` の `LowCardinality` 修飾子は無視されます。

<div id="projection-column-names-changes">
  ### PROJECTIONのカラム名に関する変更
</div>

PROJECTION名の計算時には、別名は展開されません。

```sql theme={null}
SELECT
    1 + 1 AS x,
    x + 1
SETTINGS enable_analyzer = 0
FORMAT PrettyCompact

   ┌─x─┬─plus(plus(1, 1), 1)─┐
1. │ 2 │                   3 │
   └───┴─────────────────────┘

SELECT
    1 + 1 AS x,
    x + 1
SETTINGS enable_analyzer = 1
FORMAT PrettyCompact

   ┌─x─┬─plus(x, 1)─┐
1. │ 2 │          3 │
   └───┴────────────┘
```

<div id="incompatible-function-arguments-types">
  ### 互換性のない関数引数の型
</div>

アナライザでは、型推論はクエリ分析の初期段階で行われます。
この変更により、型チェックは短絡評価の前に行われるため、`if` 関数の引数は常に共通のスーパータイプを持っている必要があります。

たとえば、次のクエリは `There is no supertype for types Array(UInt8), String because some of them are Array and some of them are not` というエラーで失敗します。

```sql theme={null}
SELECT toTypeName(if(0, [2, 3, 4], 'String'))
```

<div id="heterogeneous-clusters">
  ### 異種クラスター
</div>

アナライザによって、クラスター内のサーバー間通信プロトコルが大きく変更されます。そのため、`enable_analyzer` 設定の値が異なるサーバー間では、分散クエリを実行できません。

<div id="mutations-are-interpreted-by-previous-analyzer">
  ### ミューテーションは従来のアナライザで解釈されます
</div>

ミューテーションでは、現在も従来のアナライザが使用されます。
そのため、ClickHouse SQL の新機能の一部はミューテーションでは使用できません。たとえば、`QUALIFY` 句です。
状況は[こちら](https://github.com/ClickHouse/ClickHouse/issues/61563)で確認できます。

<div id="unsupported-features">
  ### サポートされていない機能
</div>

現在アナライザがサポートしていない機能は、以下のとおりです。

* Annoy 索引。
* Hypothesis 索引。[こちら](https://github.com/ClickHouse/ClickHouse/pull/48381)で実装が進められています。
* Window view はサポートされていません。今後もサポートされる予定はありません。

<div id="cloud-migration">
  ## Cloud 移行
</div>

新たな機能とパフォーマンスの最適化をサポートするため、現在無効になっているすべてのインスタンスでアナライザを有効化しています。この変更により SQL のスコープ規則がより厳格になり、準拠していないクエリはお客様側で手動で更新する必要があります。

<div id="migration-workflow">
  ### 移行ワークフロー
</div>

1. `normalized_query_hash` で `system.query_log` をフィルタリングし、クエリを特定します。

```sql theme={null}
SELECT query 
FROM clusterAllReplicas(default, system.query_log)
WHERE normalized_query_hash='{hash}' 
LIMIT 1 
SETTINGS skip_unavailable_shards=1
```

2. これらの設定を追加してアナライザを有効にし、クエリを実行します。

```sql theme={null}
SETTINGS
    enable_analyzer=1,
    analyzer_compatibility_join_using_top_level_identifier=1
```

3. クエリを見直して結果を検証し、アナライザを無効にした場合に生成される出力と一致することを確認します。

社内テストで頻繁に見られた主な非互換性については、以下を参照してください。

<div id="unknown-expression-identifier">
  ### 不明な式識別子
</div>

エラー: `Unknown expression identifier ... in scope ... (UNKNOWN_IDENTIFIER)`. 例外コード: 47

原因: フィルター内で計算済みの別名を参照する、曖昧なサブクエリの投影、「動的」な CTE スコープを使うといった、非標準で緩い従来の動作に依存するクエリは、現在では無効として正しく判定され、即座に拒否されます。

解決策: SQL を次のように修正してください。

* フィルター条件: 結果に対して絞り込む場合は、条件を WHERE から HAVING に移します。元データに対して絞り込む場合は、WHERE 句に同じ式を明示的に記述します。
* サブクエリのスコープ: 外側のクエリで必要になるすべてのカラムを明示的に選択します。
* JOIN の結合キー: キーが別名の場合は、USING ではなく完全な式を指定した ON を使用します。
* 外側のクエリでは、その内部のテーブルではなく、サブクエリ/CTE 自体の別名を参照します。

<div id="non-aggregated-columns-in-group-by">
  ### GROUP BY における非集計カラム
</div>

エラー: `Column ... is not under aggregate function and not in GROUP BY keys (NOT_AN_AGGREGATE)`. Exception code: 215

原因: 旧アナライザでは、GROUP BY 句に含まれていないカラムも SELECT できていました (多くの場合、任意の値が選ばれていました) 。アナライザは標準 SQL に従うため、SELECT するすべてのカラムは、集計関数を適用するか、グルーピングキーである必要があります。

解決策: カラムを `any()` または `argMax()` で囲むか、GROUP BY に追加してください。

```sql theme={null}
/* 元のクエリ */
-- device_id は曖昧です
SELECT user_id, device_id FROM table GROUP BY user_id

/* 修正後のクエリ */
SELECT user_id, any(device_id) FROM table GROUP BY user_id
-- または
SELECT user_id, device_id FROM table GROUP BY user_id, device_id
```

<div id="non-aggregated-columns-in-having">
  ### HAVING 内の非集約カラム
</div>

エラー: `Column ... is not under aggregate function and not in GROUP BY keys (NOT_AN_AGGREGATE)`。Exception code: 215

原因: 以前のアナライザは、`HAVING` 内の非集約の AND 条件を暗黙的に `WHERE` へ移動し、集約前のフィルターとして扱っていました。アナライザは Standard SQL に従うため、`HAVING` で参照できるのは集約キーと集約関数のみです。

解決策: 述語を手動で `HAVING` から `WHERE` に移すか、`analyzer_compatibility_allow_non_aggregate_in_having = 1` (ClickHouse `26.7` 以降で利用可能) を有効にして、移行を補助するためにレガシーな書き換えを復元してください。この互換性設定は、`WITH CUBE`、`WITH ROLLUP`、`WITH TOTALS`、`GROUPING SETS` では無視されます。集約、`grouping`、または非決定論的関数を含む条件は `HAVING` に残ります。いずれかの条件にウィンドウ関数または状態を持つ関数 (たとえば `rowNumberInBlock`) が含まれる場合、書き換えは `HAVING` 全体で無効になり、レガシーの動作と一致します。

```sql theme={null}
/* ORIGINAL QUERY */
SELECT category, sum(value) FROM t GROUP BY category HAVING service = 'svc1';

/* FIXED QUERY */
SELECT category, sum(value) FROM t WHERE service = 'svc1' GROUP BY category;
```

<div id="duplicate-cte-names">
  ### 重複するCTE名
</div>

エラー: `CTE with name ... already exists (MULTIPLE_EXPRESSIONS_FOR_ALIAS)`。Exception code: 179

原因: 以前のアナライザでは、同じ名前の共通テーブル式 (WITH ...) を複数定義し、先に定義したものを後から定義したものでシャドーイングすることが許可されていました。アナライザでは、このような曖昧さは許可されません。

解決策: 重複するCTEの名前を変更して、一意にしてください。

```sql theme={null}
/* 元のクエリ */
WITH 
  data AS (SELECT 1 AS id), 
  data AS (SELECT 2 AS id) -- 再定義
SELECT * FROM data;

/* 修正後のクエリ */
WITH 
  raw_data AS (SELECT 1 AS id), 
  processed_data AS (SELECT 2 AS id)
SELECT * FROM processed_data;
```

<div id="ambiguous-column-identifiers">
  ### 曖昧なカラム識別子
</div>

エラー: `JOIN [JOIN TYPE] ambiguous identifier ... (AMBIGUOUS_IDENTIFIER)` Exception code: 207

原因: クエリで、どのテーブルのものかを指定せずに、JOIN 内の複数のテーブルに存在するカラム名を参照しています。古いアナライザは内部ロジックに基づいてカラムを推測することがよくありましたが、アナライザでは明示的に名前を指定する必要があります。

解決策: `table_alias.column_name` のように、カラムを完全修飾してください。

```sql theme={null}
/* 元のクエリ */
SELECT table1.ID AS ID FROM table1, table2 WHERE ID...

/* 修正後のクエリ */
SELECT table1.ID AS ID_RENAMED FROM table1, table2 WHERE ID_RENAMED...
```

<div id="invalid-usage-of-final">
  ### FINAL の無効な使用
</div>

エラー: `Table expression modifiers FINAL are not supported for subquery...` または `Storage ... doesn't support FINAL` (`UNSUPPORTED_METHOD`)。例外コード: 1, 181

原因: FINAL はテーブルストレージ (特に \[Shared]ReplacingMergeTree) の修飾子です。アナライザは、次の対象に FINAL を適用すると拒否します。

* サブクエリまたは派生テーブル (例: FROM (SELECT ...) FINAL) 。
* FINAL をサポートしていないテーブルエンジン (例: SharedMergeTree) 。

解決策: FINAL はサブクエリ内のソーステーブルにのみ適用するか、エンジンがサポートしていない場合は削除してください。

```sql theme={null}
/* 元のクエリ */
SELECT * FROM (SELECT * FROM my_table) AS subquery FINAL ...

/* 修正後のクエリ */
SELECT * FROM (SELECT * FROM my_table FINAL) AS subquery ...
```

<div id="countdistinct-case-insensitivity">
  ### `countDistinct()` 関数の大文字・小文字の区別
</div>

エラー: `Function with name countdistinct does not exist (UNKNOWN_FUNCTION)`。Exception code: 46

原因: 関数名では大文字・小文字が区別されるか、アナライザで厳密にマッピングされます。`countdistinct` (すべて小文字) は、今後は自動的に解決されません。

対処法: 標準の `countDistinct` (camelCase) または ClickHouse 固有の `uniq` を使用してください。
