Skip to main content
materialized_view マテリアライゼーションは、既存のソーステーブルに対する SELECT である必要があります。PostgreSQL とは異なり、ClickHouse の materialized view は「静的」ではなく (対応する REFRESH 操作もありません) 、挿入トリガー として機能します。つまり、ソーステーブルに挿入された行に対して、定義された SELECT 変換を適用し、その結果として新しい行をターゲットテーブルに挿入します。ClickHouse における materialized view の動作の詳細については、ClickHouse materialized view のドキュメントを参照してください。
一般的なマテリアライゼーションの概念と共通の設定 (engine、order_by、partition_by など) については、Materializations ページを参照してください。

ターゲットテーブルの管理方法

materialized_view マテリアライゼーションを使用する場合、dbt-clickhouse では materialized view と、変換後の行が挿入される ターゲットテーブル の両方を作成する必要があります。ターゲットテーブルの管理方法は 2 つあります。 どちらのアプローチを選ぶかによって、スキーマ変更、フルリフレッシュ、複数 MV 構成の扱い方が変わります。以下のセクションでは、それぞれのアプローチについて詳しく説明します。

暗黙的ターゲットによるマテリアライズ

これはデフォルトの動作です。materialized_view モデルを定義すると、アダプターは次の処理を行います。
  1. モデル名で ターゲットテーブル を作成します
  2. <model_name>_mv という名前の ClickHouse materialized view を作成します
ターゲットテーブルのスキーマは、MV の SELECT ステートメント内のカラムから推論されます。すべてのリソース (ターゲットテーブル + MV) は、同じモデル設定を共有します。
追加の例については、テストファイルを参照してください。
モデルコントラクトを適用すると、ターゲットテーブルでカラム単位のcodecttlを定義することもできます。詳しくは、カラム設定を参照してください。

複数のmaterialized view

ClickHouse では、複数のmaterialized viewから同じターゲットテーブルにレコードを書き込めます。dbt-clickhouse で暗黙的ターゲットのアプローチを使ってこれをサポートするには、モデルファイル内で UNION を構成し、各materialized view の SQL を --my_mv_name:begin--my_mv_name:end の形式のコメントで囲みます。 たとえば、以下の例では 2 つのmaterialized view が作成され、どちらもそのモデルの同じ宛先テーブルにデータを書き込みます。materialized view の名前は <model_name>_mv1 および <model_name>_mv2 の形式になります。
複数のmaterialized view (MV) を持つモデルを更新する際、特にMV名の1つを変更した場合、 dbt-clickhouse は古いMVを自動的に削除しません。代わりに、 次の警告が表示されます。Warning - Table <previous table name> was detected with the same pattern as model name <your model name> but was not found in this run. In case it is a renamed mv that was previously part of this model, drop it manually (!!!)

ターゲットテーブルのスキーマを段階的に変更する方法

dbt-clickhouse version 1.9.8 以降では、dbt run が MV の SQL 内で異なるカラムを検出した際に、ターゲットテーブルのスキーマをどのように段階的に変更するかを制御できます。
デフォルトでは、dbt はターゲットテーブルにいかなる変更も適用しません (設定値は ignore) 。ただし、この設定を変更することで、incremental モデルにおける on_schema_change 設定と同じ挙動にできます。 また、この設定は安全策として使うこともできます。これを fail に設定すると、MV の SQL 内のカラムが、最初の dbt run で作成されたターゲットテーブルと異なる場合、ビルドは失敗します。

データのキャッチアップ

デフォルトでは、materialized view (MV) を作成または再作成する際、MV 自体が作成される前に、まずターゲットテーブルへ過去のデータが投入されます (catchup=True) 。この動作は、catchup 設定を False にすることで無効にできます。
フルリフレッシュ時のデータ損失リスクcatchup: Falsedbt run --full-refresh と併用すると、ターゲットテーブル内の既存データはすべて破棄されます。テーブルは空の状態で再作成され、その後は新しいデータのみを取り込みます。後で過去データが必要になる可能性がある場合は、バックアップがあることを確認してください。

明示的ターゲットによるマテリアライゼーション (ベータ)

ベータこの機能はベータ版で、dbt-clickhouse version 1.10 から利用できます。API はコミュニティからのフィードバックに応じて変更される可能性があります。
デフォルトでは、dbt-clickhouse は単一のモデル内でターゲットテーブルと materialized view の両方を作成・管理します (上で説明した 暗黙的ターゲット アプローチ) 。このアプローチにはいくつかの制限があります。
  • すべてのリソース (ターゲットテーブル + MV) は同じ設定を共有します。複数の MV が同じターゲットテーブルを参照する場合は、UNION ALL 構文を使ってまとめて定義する必要があります。
  • これらのリソースを個別に反復処理することはできず、すべて同じモデルファイルで管理する必要があります。
  • 各 MV の名前を簡単に制御することはできません。
  • すべての設定がターゲットテーブルと MV の間で共有されるため、各リソースを個別に設定したり、どの設定がどのリソースに対応するのかを把握したりするのが難しくなります。
明示的ターゲット 機能を使うと、ターゲットテーブルを通常の table マテリアライゼーションとして個別に定義し、その後 materialized view のモデルから参照できます。

利点

  • リソースを完全に分離: 各リソースを個別に定義できるようになり、可読性が向上します
  • dbt と CH の間で 1:1 のリソース対応: dbt のツールを使って、それぞれを個別に管理し、反復的に改善できるようになりました。
  • 異なる設定が利用可能に: それぞれに異なる設定を適用できるようになりました。
  • 命名規則を維持する必要がなくなる: MV 用に _mv を付けたカスタム名ではなく、指定した名前で各リソースが作成されるようになりました。

制限事項

  • ターゲットテーブルの定義は dbt の考え方にはあまりなじみません。これはソーステーブルを読み取る SQL ではないため、この部分では dbt の検証が効きません。一方で、MV の SQL 自体は引き続き dbt のユーティリティで検証され、ターゲットテーブルのカラムとの互換性は CH レベルで検証されます。
  • ref() 関数の制約に起因するいくつかの問題が見つかっています: モデル同士を参照するためにこれを使う必要がありますが、参照できるのは上流モデルのみで、下流モデルは参照できません。そのため、この実装ではいくつかの問題が生じます。私たちは dbt-core リポジトリに issue を作成しており、現在 解決策を検討するために dbt 側と協議しています (dbt-labs/dbt-core#12319):
    • ref() を config ブロック内で呼び出すと、共有先のモデルではなく現在のモデルが返されます。このため config() セクション内では定義できず、この依存関係を追加するにはコメントを使わざるを得ません。これは、dbt のドキュメントで示されている 「—depends_on:」アプローチ と同じパターンです。
    • ref() によってターゲットテーブルが先に作成されるため、その点では期待どおりに動作しますが、生成されたドキュメントの依存関係チャートでは、ターゲットテーブルは下流ではなく別の上流依存関係として描画されるため、少し分かりにくくなります。
    • unit-test でも、本来はそこから読み取る想定ではないにもかかわらず、ターゲットテーブル用のデータを定義する必要があります。回避策としては、このテーブルのデータを空のままにしておくだけです。

使用方法

Step 1: ターゲットテーブルを通常のテーブルモデルとして定義する モデル events_daily.sql:
これは、制限事項のセクションで説明している回避策です。ここでは dbt の検証の一部が失われる可能性がありますが、スキーマ自体は引き続き ClickHouse 側でチェックされます。 ステップ 2: ターゲットテーブルを指す materialized view を定義する たとえば、同じターゲットテーブルを参照する場合でも、このように異なるモデルで異なる MV を定義できます。新しい {{ materialization_target_table(ref('events_daily')) }} マクロ呼び出しに注目してください。これは、MV のターゲットテーブルを設定します。 モデル page_events_aggregator.sql:
モデル mobile_events_aggregator.sql

設定オプション

明示的ターゲットテーブルを使用する場合、一般的な materialization 設定およびテーブル固有の設定に加えて、以下の設定が適用されます。 ターゲットテーブル側 (materialized='table') : materialized view 側 (materialized='materialized_view') :
通常、True に設定するのは、MV 側の catchup と、そのターゲットテーブル側の repopulate_from_mvs_on_full_refresh のどちらか一方だけです。両方を True にすると、データが重複する可能性があります。

主な操作

明示的ターゲットを使用した完全リフレッシュ

--full-refresh を使用すると、明示的ターゲットのテーブルは再作成されます (この処理中にインジェストが行われると、データが失われる可能性があります) 。挙動は設定によって異なります。 オプション 1: デフォルトの --full-refresh の動作。すべてが再作成されますが、MV の再作成中はターゲットテーブルが空、または一部しか読み込まれていない状態になります。 すべてが削除され、再作成されます。MV の SQL を使ってデータを再度 insert したい場合は、設定 catchup=True のままにしてください。
オプション 2: ターゲットテーブルを再作成したいが、MV の再作成中に空のデータを読みたくない場合。 まず MV の SQL を更新する必要がある場合は、先にそれぞれに catchup=False を設定し、その後 MV に対して dbt run または dbt run --full-refresh を実行できます。ターゲットテーブルに対して --full-refresh を実行する前に、MV が作成されていることを確認してください。これは ClickHouse 上の MV 定義が使われるためです。 ターゲットテーブルの model で repopulate_from_mvs_on_full_refresh=True を設定します。dbt run --full-refresh を実行すると、次の処理が行われます。
  1. 新しい一時テーブルを作成する
  2. 各 MV の SQL を使って INSERT-SELECT を実行する
  3. テーブルをアトミックに入れ替える
そのため、MV の再作成中でもテーブルで空のデータが見えることはありません。

ターゲットテーブルの変更

--full-refresh なしで MV のターゲットテーブルを変更することはできません。materialization_target_table() の参照先を変更したあとに通常の dbt run を実行すると、ターゲットが変更されていることを示すエラーメッセージが表示され、ビルドは失敗します。 ターゲットを変更するには、次の手順を実行します。
  1. materialization_target_table() の呼び出しを更新します
  2. dbt run --full-refresh -s your_mv_model を実行します

よくある問題の対処法

run の実行中または実行後にターゲットテーブルが空になる

これが発生する理由はいくつかあります。
  • materialized view が catchup=False に設定されているか、ターゲットテーブルが repopulate_from_mvs_on_full_refresh=False に設定されている可能性があります。この場合、materialized view の作成時やターゲットテーブルの再作成時に バックフィル は実行されません。これは想定どおりの動作です。したがって、materialized view の SQL を使ってデータを再度 insert したい場合は、materialized view で catchup=True (デフォルト値) を設定するか、ターゲットテーブルで repopulate_from_mvs_on_full_refresh=True を設定してください。重複を避けるため、両方を同時に有効にしないでください。詳しくは、configuration セクション を参照してください。
  • dbt run --full-refresh の実行中に materialized view がデフォルトの catchup=True を使用している場合、ターゲットは再作成され、MV によってデータが順次再度 insert されます。この状況を避けるには、明示的ターゲットでの Full refresh を参照してください。

repopulate_from_mvs_on_full_refresh=True が設定されたターゲットテーブルで dbt run --full-refresh を実行すると、現在プロジェクト内にある SQL ではなく、古い materialized view バージョンのロジックが使用されます

repopulate_from_mvs_on_full_refresh=True は、ClickHouse にすでに定義されている既存の MV の SQL を使用します。新しい materialized view の定義が使われるようにするには、ターゲットテーブルで dbt run --full-refresh を実行する前に、各 materialized view に対して dbt run を実行してください。

実行後に重複データが発生する

考えられる原因:
  • materialized view の catchup=True と、ターゲットテーブルの repopulate_from_mvs_on_full_refresh=True の両方が有効になっている可能性があります。実行したい処理に応じて、どちらか一方のみを有効にしてください。詳細は設定セクションを参照してください。
  • ターゲットテーブルが WHERE 0 を付けて定義されていません。ターゲットテーブルは空の状態で作成する必要がありますが、WHERE 0 が含まれていないと内部クエリによってデータが挿入される場合があります。該当する句が含まれていることを確認してください。

dbt run --full-refresh の実行後、進行中のインジェスト中にデータが失われる

dbt run --full-refresh の実行後、ソーステーブルの一部の行がターゲットテーブルに反映されていません。 ClickHouse の materialized view は挿入トリガーとして機能し、存在している間しかデータを取り込みません。フルリフレッシュ中は、MV が削除されてから再作成されるまでの短い時間帯 (「ブラインドウィンドウ」) が発生します。この時間帯にソーステーブルへ挿入された行は取り込まれません。詳細については、進行中のインジェスト中の挙動 セクションを参照してください。

デバッグ手法

ClickHouse で MV の現在の書き込み先を確認する

materialized view の書き込み先を確認するには、system.tables にクエリします。

dbt がテーブルを materialized view のターゲットとして認識しているか確認する

dbt の実行中に、次のログメッセージが出力されるか確認してください。
Table <table_name> is used as a target by a dbt-managed materialized view. Defaulting mv_on_schema_change to “fail” to prevent data loss.
このメッセージが表示された場合、dbt はそのテーブルが少なくとも 1 つの dbt 管理下の materialized view のターゲットになっていることを検出しています。このメッセージが表示されるはずなのに見当たらない場合は、次の点を確認してください。
  • materialized view モデルで {{ materialization_target_table(ref('your_target')) }} が正しく定義されている
  • materialized view モデルの設定に materialized='materialized_view' が含まれている
  • materialized view とターゲットテーブルの両方が、少なくとも 1 回は実行されている

暗黙的ターゲットから明示的ターゲットへの移行

暗黙的ターゲット方式を使用している既存の materialized view モデルがあり、明示的ターゲット方式に移行したい場合は、以下の手順に従ってください。 1. ターゲットテーブルモデルを作成する 現在の MV ターゲットテーブルと同じスキーマを定義する、新しい materialized='table' モデルファイルを作成します。空のテーブルを作成するには、WHERE 0 句を使用します。名前は現在の暗黙的 materialized view モデルと同じにしてください。これにより、以後はこのモデルを使ってターゲットテーブルを更新できるようになります。
2. MV モデルを更新する MV の SQL と、新しいターゲットテーブルを参照する materialization_target_table() マクロ呼び出しをそれぞれ含む新しいモデルを作成します。以前に UNION ALL を使用していた場合は、その部分とコメントを削除します。 モデル名は、次の命名規則に従う必要があります。
  • MV が 1 つだけ定義されていた場合、名前は <old_model_name>_mv です
  • 複数の MV が定義されていた場合、各 MV の名前は <old_model_name>_mv_<name_in_comments> です
変更前の my_model.sql (暗黙的ターゲット、UNION ALL を使った単一モデル) :
変更後 (明示的ターゲット、分離されたモデルファイル) :
3. 必要に応じて、明示的ターゲット セクションの手順に従ってそれらの調整を繰り返します。

暗黙的ターゲット方式と明示的ターゲット方式の挙動比較

一般的な挙動

アクティブなインジェスト中の動作

モデルを反復的に改善していく際には、各操作が挿入中のデータにどのように影響するかを理解しておく必要があります。
  • ClickHouse の materialized view は insert trigger として機能するため、存在している間のデータしか取り込みません。materialized view が削除されて再作成されると (たとえば --full-refresh 中) 、その間にソーステーブルへ挿入された行は materialized view では処理されません。この状態は、materialized view が「blind」であると表現されます。
  • catchup プロセスはいずれも、materialized view の SQL を使った INSERT INTO ... SELECT 操作に基づいており、materialized view の動作とは独立しています。INSERT が開始されると、それ以降の新しいデータはその処理では取り込まれませんが、アタッチされた materialized view では取り込まれます。
次の表は、ソーステーブルで insert が継続的に発生している場合における、各操作の安全性を要約したものです。

暗黙的ターゲットでの操作

明示的ターゲットの操作

materialized view モデル: ターゲットテーブルモデル:
本番環境でインジェストがアクティブな場合の推奨事項
  • 可能であれば、dbt 操作中はインジェストを一時停止してください: こうすることで、すべての操作が安全になり、データが失われることもありません。
  • 可能であれば、ターゲットテーブルでは重複排除可能なエンジン (例: ReplacingMergeTree) を使用してください。これにより、キャッチアップの重複で発生しうる重複データに対応できます。
  • 可能であれば ALTER TABLE ... MODIFY QUERY (--full-refresh なしの通常の dbt run) を優先してください — これは常に安全です。
  • dbt 操作中の問題が生じうる windowに注意してください。

リフレッシャブルmaterialized view

リフレッシャブルmaterialized view は、ClickHouse における特殊な種類の materialized view で、クエリを定期的に再実行してその結果を保存します。これは、他のデータベースにおける materialized view の動作に似ています。リアルタイムの挿入トリガーではなく、定期的なスナップショットや集計が必要なシナリオで役立ちます。
リフレッシャブルmaterialized view は、暗黙的ターゲット明示的ターゲット両方のアプローチで使用できます。refreshable 設定は、ターゲットテーブルの管理方法とは独立しています。
リフレッシャブルmaterialized view を使用するには、次のオプションを含む refreshable 設定オブジェクトを MVモデルに追加します。

暗黙的ターゲットを使った例

明示的ターゲットの例

リフレッシュパラメータの更新

dbt-clickhouse 1.10.2 以降では、既存のリフレッシャブルmaterialized view のリフレッシュパラメータ (intervalrandomizedepends_on) の変更は、通常の dbt run 実行時に ALTER TABLE ... MODIFY REFRESH を使用してインプレースで適用されます。materialized view は削除も再作成もされないため、データが失われることはなく、変更の適用中に「ブラインドウィンドウ」が生じることもありません。 一部の変更はインプレースで適用できません。dbt はこれらを暗黙的に無視するのではなく、dbt run --full-refresh を使用するよう求めるエラーで実行を失敗させます。
  • 通常のmaterialized view をリフレッシャブルなものに変換する場合 (refreshable config を追加する場合) 、またはその逆の場合 (これを削除する場合) 。
  • ClickHouse では MODIFY REFRESHAPPEND を追加または削除できないため、append 設定を変更する場合。
--full-refresh ではmaterialized view が削除・再作成され、アクティブなインジェスト中の動作で説明されている影響があることに注意してください。

制限事項

  • 依存関係を持つリフレッシャブルmaterialized view (MV) を ClickHouse で作成する際、作成時点で指定した依存関係が存在しなくても、ClickHouse は エラーを返しません。代わりに、そのリフレッシャブル MV は非アクティブな状態のままとなり、依存関係が満たされて更新処理やリフレッシュを開始できるようになるまで待機します。 この動作は仕様ですが、必要な依存関係への対応が 速やかに行われない場合、データが利用可能になるまでに遅れが生じる可能性があります。リフレッシャブル materialized view を作成する前に、すべての依存関係が正しく定義され、存在していることを確認してください。
  • 現時点では、mv とその依存関係の間に実際の「dbt linkage」はないため、作成順序は 保証されません。
  • refreshable 機能は、同じターゲットモデルに向けられた複数の mvs ではテストされていません。
最終更新日 2026年8月26日