> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-mintlify-8c05c8a2.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> プロジェクションの操作に関するドキュメント

# プロジェクション

このページでは、プロジェクションの概要、使用方法、およびプロジェクションを操作するための各種オプションについて説明します。

<div id="overview">
  ## プロジェクションの概要
</div>

プロジェクションは、クエリ実行を最適化するフォーマットでデータを格納します。この機能は、次のような場合に役立ちます。

* 主キーに含まれないカラムに対してクエリを実行する
* カラムを事前集計し、計算量と IO の両方を削減する

テーブルには 1 つ以上のプロジェクションを定義でき、クエリ解析時には、スキャン対象のデータ量が最も少ないプロジェクションが、ユーザーが指定したクエリを変更することなく ClickHouse によって選択されます。

<Info>
  **ディスク使用量**

  プロジェクションは内部的に新しい非表示テーブルを作成するため、より多くの IO とディスク容量が必要になります。
  たとえば、プロジェクションで異なる主キーを定義した場合、元のテーブルのすべてのデータが複製されます。
</Info>

プロジェクションが内部でどのように動作するかについて、より技術的な詳細はこの[ページ](/ja/guides/clickhouse/data-modelling/sparse-primary-indexes#option-3-projections)で確認できます。

<div id="examples">
  ## プロジェクションを使う
</div>

<div id="example-filtering-without-using-primary-keys">
  ### 主キーを使わないフィルタリングの例
</div>

テーブルを作成します。

```sql theme={null}
CREATE TABLE visits_order
(
   `user_id` UInt64,
   `user_name` String,
   `pages_visited` Nullable(Float64),
   `user_agent` String
)
ENGINE = MergeTree()
PRIMARY KEY user_agent
```

`ALTER TABLE` を使用すると、既存のテーブルにプロジェクションを追加できます。

```sql theme={null}
ALTER TABLE visits_order ADD PROJECTION user_name_projection (
    SELECT *
    ORDER BY user_name
)

ALTER TABLE visits_order MATERIALIZE PROJECTION user_name_projection
```

データの挿入:

```sql theme={null}
INSERT INTO visits_order SELECT
    number,
    'test',
    1.5 * (number / 2),
    'Android'
FROM numbers(1, 100);
```

プロジェクションを使うと、元のテーブルで `user_name` が `PRIMARY_KEY` として定義されていなくても、`user_name` で高速に絞り込めます。
クエリ時には、データが `user_name` 順に並んでいるため、プロジェクションを使ったほうが処理されるデータ量が少なくなると ClickHouse が判断します。

```sql theme={null}
SELECT
    *
FROM visits_order
WHERE user_name='test'
LIMIT 2
```

クエリでプロジェクションが使用されているかを確認するには、`system.query_log` テーブルを確認します。`projections` フィールドには、使用されたプロジェクション名が入り、何も使用されていない場合は空です。

```sql theme={null}
SELECT query, projections FROM system.query_log WHERE query_id='<query_id>'
```

<div id="example-pre-aggregation-query">
  ### 事前集計クエリの例
</div>

プロジェクション `projection_visits_by_user` を持つテーブルを作成します:

```sql theme={null}
CREATE TABLE visits
(
   `user_id` UInt64,
   `user_name` String,
   `pages_visited` Nullable(Float64),
   `user_agent` String,
   PROJECTION projection_visits_by_user
   (
       SELECT
           user_agent,
           sum(pages_visited)
       GROUP BY user_id, user_agent
   )
)
ENGINE = MergeTree()
ORDER BY user_agent
```

データを挿入します:

```sql theme={null}
INSERT INTO visits SELECT
    number,
    'test',
    1.5 * (number / 2),
    'Android'
FROM numbers(1, 100);
```

```sql theme={null}
INSERT INTO visits SELECT
    number,
    'test',
    1. * (number / 2),
   'IOS'
FROM numbers(100, 500);
```

フィールド `user_agent` を使って、`GROUP BY` を含む最初のクエリを実行します。
このクエリでは、事前集計が一致しないため、定義したプロジェクションは使用されません。

```sql theme={null}
SELECT
    user_agent,
    count(DISTINCT user_id)
FROM visits
GROUP BY user_agent
```

projection を利用するには、事前集計と `GROUP BY` のフィールドの一部またはすべてを選択するクエリを実行できます。

```sql theme={null}
SELECT
    user_agent
FROM visits
WHERE user_id > 50 AND user_id < 150
GROUP BY user_agent
```

```sql theme={null}
SELECT
    user_agent,
    sum(pages_visited)
FROM visits
GROUP BY user_agent
```

前述のとおり、プロジェクションが使用されたかどうかは `system.query_log` テーブルを確認することで把握できます。
`projections` フィールドには、使用されたプロジェクションの名前が表示されます。
プロジェクションが使用されていない場合、このフィールドは空になります。

```sql theme={null}
SELECT query, projections FROM system.query_log WHERE query_id='<query_id>'
```

<div id="projection-indexes">
  ### プロジェクション索引の作成と使用
</div>

[プロジェクション索引](/ja/reference/engines/table-engines/mergetree-family/mergetree#projection-index)を作成するには、次のようにします。

```sql theme={null}
CREATE TABLE events
(
    `event_time` DateTime,
    `event_id` UInt64,
    `user_id` UInt64,
    `huge_string` String,
    PROJECTION order_by_user_id INDEX user_id TYPE basic
)
ENGINE = MergeTree()
ORDER BY (event_id);
```

<details markdown="1">
  <summary>明示的な`_part_offset`フィールドを持つプロジェクションの作成</summary>

  プロジェクションは、次の構文でも作成できます (非推奨) :

  ```sql theme={null}
  CREATE TABLE events
  (
      `event_time` DateTime,
      `event_id` UInt64,
      `user_id` UInt64,
      `huge_string` String,
      PROJECTION order_by_user_id
      (
          SELECT
              _part_offset
          ORDER BY user_id
      )
  )
  ENGINE = MergeTree()
  ORDER BY (event_id);
  ```
</details>

いくつかのサンプルデータを挿入します:

```sql theme={null}
INSERT INTO events SELECT * FROM generateRandom() LIMIT 100000;
```

`_part_offset` フィールドは、マージやミューテーションを経てもその値が保持されるため、セカンダリ索引で有用です。これはクエリで活用できます:

```sql theme={null}
SELECT
    count()
FROM events
WHERE _part_starting_offset + _part_offset IN (
    SELECT _part_starting_offset + _part_offset
    FROM events
    WHERE user_id = 42
)
SETTINGS enable_shared_storage_snapshot_in_query = 1
```

<div id="manipulating-projections">
  ## プロジェクションの操作
</div>

[プロジェクション](/ja/reference/engines/table-engines/mergetree-family/mergetree#projections)に対しては、次の操作を実行できます:

<div id="add-projection">
  ### ADD PROJECTION
</div>

以下のステートメントを使用して、テーブルのメタデータにプロジェクションの説明を追加します。

```sql theme={null}
ALTER TABLE [db.]name [ON CLUSTER cluster] ADD PROJECTION [IF NOT EXISTS] name ( SELECT <COLUMN LIST EXPR> [GROUP BY] [ORDER BY] ) [WITH SETTINGS ( setting_name1 = setting_value1, setting_name2 = setting_value2, ...)]
```

<div id="with-settings">
  #### `WITH SETTINGS` 句
</div>

`WITH SETTINGS` では**プロジェクションレベルの設定**を定義し、プロジェクションでのデータの格納方法 (たとえば `index_granularity` や `index_granularity_bytes`) をカスタマイズできます。
これらは **MergeTree のテーブル設定** に直接対応していますが、適用されるのは**このプロジェクションのみ**です。

例:

```sql theme={null}
ALTER TABLE t
ADD PROJECTION p (
    SELECT x ORDER BY x
) WITH SETTINGS (
    index_granularity = 4096,
    index_granularity_bytes = 1048576
);
```

プロジェクション設定は、検証ルールに従い、当該プロジェクションに適用される実効的なテーブル設定を上書きします (例: 無効または互換性のない上書きは拒否されます) 。

<div id="drop-projection">
  ### DROP PROJECTION
</div>

以下のステートメントを使用して、テーブルのメタデータからPROJECTIONの定義を削除し、ディスク上のPROJECTIONファイルを削除します。
これは [ミューテーション](/ja/reference/statements/alter/index#mutations) として実装されています。

```sql theme={null}
ALTER TABLE [db.]name [ON CLUSTER cluster] DROP PROJECTION [IF EXISTS] name
```

<div id="materialize-projection">
  ### MATERIALIZE PROJECTION
</div>

以下のステートメントを使用して、パーティション `partition_name` のPROJECTION `name` を再構築します。
これは [ミューテーション](/ja/reference/statements/alter/index#mutations) として実装されています。

```sql theme={null}
ALTER TABLE [db.]table [ON CLUSTER cluster] MATERIALIZE PROJECTION [IF EXISTS] name [IN PARTITION partition_name]
```

<div id="clear-projection">
  ### CLEAR PROJECTION
</div>

説明を削除せずにディスク上のPROJECTIONファイルを削除するには、以下のステートメントを使用します。
これは [ミューテーション](/ja/reference/statements/alter/index#mutations) として実装されています。

```sql theme={null}
ALTER TABLE [db.]table [ON CLUSTER cluster] CLEAR PROJECTION [IF EXISTS] name [IN PARTITION partition_name]
```

`ADD`、`DROP`、`CLEAR` コマンドは、メタデータの変更またはファイルの削除だけを行うという意味で軽量です。
また、これらのコマンドはレプリケーションされ、ClickHouse Keeper または ZooKeeper を介してPROJECTIONのメタデータを同期します。

<Note>
  PROJECTIONの操作は、[`*MergeTree`](/ja/reference/engines/table-engines/mergetree-family/mergetree) エンジンのテーブルでのみサポートされます ([レプリケーション対応](/ja/reference/engines/table-engines/mergetree-family/replication)のバリアントを含む) 。
</Note>

<div id="control-projections-merges">
  ### プロジェクションのマージ動作を制御する
</div>

クエリを実行すると、ClickHouse は元のテーブルまたはそのプロジェクションのいずれから読み取るかを選択します。
元のテーブルとそのプロジェクションのどちらから読み取るかの判断は、各テーブルパーツごとに個別に行われます。
ClickHouse は一般に、できるだけ少ないデータを読み取ることを目指しており、たとえばパーツの主キーをサンプリングするなど、最適な読み取り元のパーツを特定するためにいくつかの手法を用います。
場合によっては、元のテーブルのパーツに対応するプロジェクションパーツが存在しないことがあります。
これは、たとえば SQL でテーブルにプロジェクションを作成する処理がデフォルトで「遅延的」だからです。つまり、新たに挿入されるデータにのみ影響し、既存のパーツは変更されません。

いずれかのプロジェクションには事前計算済みの集約値がすでに含まれているため、ClickHouse はクエリ実行時に再度集約を行うことを避けるために、対応するプロジェクションパーツから読み取ろうとします。特定のパーツに対応するプロジェクションパーツがない場合、クエリ実行は元のパーツにフォールバックします。

しかし、元のテーブル内の行が、単純ではない data part のバックグラウンドマージによって大きく変化した場合はどうなるでしょうか。
たとえば、そのテーブルが `ReplacingMergeTree` テーブルエンジンを使用して保存されているとします。
マージ中に同じ行が複数の入力パーツで検出された場合、もっとも新しい行バージョン (もっとも最近に挿入されたパーツのもの) だけが保持され、それ以前のすべてのバージョンは破棄されます。

同様に、テーブルが `AggregatingMergeTree` テーブルエンジンを使用して保存されている場合、マージ操作によって、入力パーツ内の同じ行が主キーの値に基づいて 1 行にまとめられ、部分的な集約状態が更新されることがあります。

ClickHouse v24.8 より前では、プロジェクションパーツはメインデータと気づかれないまま同期がずれるか、あるいは更新や削除などの特定の操作をまったく実行できませんでした。これは、テーブルにプロジェクションがある場合、データベースが自動的に例外を送出していたためです。

v24.8 以降では、新しいテーブルレベル設定 [`deduplicate_merge_projection_mode`](/ja/reference/settings/merge-tree-settings#deduplicate_merge_projection_mode) により、前述の単純ではないバックグラウンドマージ操作が元のテーブルのパーツで発生した場合の動作を制御できます。

Delete mutations も、元のテーブルのパーツ内の行を削除する part merge 操作の別の例です。v24.7 以降では、論理削除によってトリガーされる delete mutations に関する動作を制御する設定もあります: [`lightweight_mutation_projection_mode`](/ja/reference/settings/merge-tree-settings#deduplicate_merge_projection_mode)。

以下は、`deduplicate_merge_projection_mode` と `lightweight_mutation_projection_mode` の両方で設定可能な値です。

* `throw` (デフォルト): 例外が送出され、プロジェクションパーツの同期ずれを防ぎます。
* `drop`: 影響を受けるプロジェクションテーブルパーツは削除されます。影響を受けたプロジェクションパーツについては、クエリは元のテーブルパーツにフォールバックします。
* `rebuild`: 影響を受けたプロジェクションパーツは、元のテーブルパーツ内のデータとの整合性を保つために再構築されます。

<div id="limitations">
  ## 制限事項
</div>

プロジェクションの`ORDER BY`句では、`ALIAS`カラムは使用できません。例:

```sql highlight={6} theme={null}
CREATE TABLE t
(
    id UInt64,
    a UInt32,
    ab_sum UInt64 ALIAS a + 1,
    PROJECTION p (SELECT a ORDER BY ab_sum)
)
ENGINE = MergeTree ORDER BY id;
-- UNKNOWN_IDENTIFIERで失敗する
```

`ALIAS` カラムは物理的に保存されず、クエリ時にその場で計算されるため、ソート式が評価されるプロジェクションパートの書き込み処理では使用できません。

代わりに、`MATERIALIZED` カラムを使用するか、式を直接埋め込んでください。

```sql theme={null}
-- MATERIALIZEDカラムを使用する場合
CREATE TABLE t
(
    id UInt64,
    a UInt32,
    ab_sum UInt64 MATERIALIZED a + 1,
    PROJECTION p (SELECT a ORDER BY ab_sum)
)
ENGINE = MergeTree ORDER BY id;

-- インライン式を使用する場合
CREATE TABLE t
(
    id UInt64,
    a UInt32,
    PROJECTION p (SELECT a ORDER BY a + 1)
)
ENGINE = MergeTree ORDER BY id;
```

<div id="see-also">
  ## 関連項目
</div>

* ["マージ時のプロジェクション制御" (ブログ記事) ](https://clickhouse.com/blog/clickhouse-release-24-08#control-of-projections-during-merges)
* ["プロジェクション" (ガイド) ](/ja/concepts/features/projections/projections#using-projections-to-speed-up-UK-price-paid)
* ["materialized view とプロジェクション"](/ja/concepts/features/projections/materialized-views-versus-projections)
