Skip to content
ClickHouse Docs
ClickHouse DocsClickHouse Docs

CREATE VIEW

新しいビューを作成します。ビューには、通常のビューmaterialized viewリフレッシャブルmaterialized view、およびウィンドウビューがあります。

通常ビュー

構文:

CREATE [OR REPLACE] VIEW [IF NOT EXISTS] [db.]table_name [(alias1 [, alias2 ...])] [ON CLUSTER cluster_name]
[DEFINER = { user | CURRENT_USER }] [SQL SECURITY { DEFINER | INVOKER | NONE }]
AS SELECT ...
[COMMENT 'comment']

通常のビューはデータを一切保存しません。アクセスされるたびに、別のテーブルから読み取るだけです。つまり、通常のビューは単なる保存クエリにすぎません。ビューから読み取る際には、この保存クエリが FROM 句内のサブクエリとして使用されます。

例として、次のようなビューを作成したとします。

CREATE VIEW view AS SELECT ...

そして、クエリを記述します:

SELECT a, b, c FROM view

このクエリは、次のサブクエリを使用した場合と完全に等価です。

SELECT a, b, c FROM (SELECT ...)

パラメーター化ビュー

パラメーター化ビューは通常のビューに似ていますが、すぐには評価されないパラメーターを指定して作成できます。 これらのビューはテーブル関数として使用でき、その際はビュー名を関数名として、パラメーター値を引数として指定します。

CREATE VIEW view AS SELECT * FROM TABLE WHERE Column1={column1:datatype1} and Column2={column2:datatype2} ...

上記により、以下のようにパラメータを置き換えることでテーブル関数として使用できる、テーブル用のビューが作成されます。

SELECT * FROM view(column1=value1, column2=value2 ...)

パラメーター化ビューはパラメーター値に依存するため、パラメーターが指定されていない場合はスキーマを持ちません。 つまり、system.columns テーブルにはパラメーター化ビューに関する情報はありません。 また、DESCRIBE クエリはパラメーターが指定されている場合にのみ機能します。

DESCRIBE view(column1=value1, column2=value2 ...)

Materialized View

CREATE MATERIALIZED VIEW [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster_name] [TO[db.]name [(columns)]] [ENGINE = engine] [POPULATE]
[REFRESH ...]
[DEFINER = { user | CURRENT_USER }] [SQL SECURITY { DEFINER | NONE }]
AS SELECT ...
[COMMENT 'comment']
CREATE OR REPLACE MATERIALIZED VIEW [db.]table_name [ON CLUSTER cluster_name] [TO[db.]name [(columns)]] [ENGINE = engine] [POPULATE]
[REFRESH ...]
[DEFINER = { user | CURRENT_USER }] [SQL SECURITY { DEFINER | NONE }]
AS SELECT ...
[COMMENT 'comment']

OR REPLACEIF NOT EXISTS は相互に排他的であり、組み合わせると構文エラーになります。

CREATE OR REPLACE MATERIALIZED VIEW

CREATE OR REPLACE MATERIALIZED VIEW は、既存のmaterialized viewと、その内部ストレージテーブル (存在する場合) をアトミックに置き換えます。この操作には、Atomic または Replicated データベースエンジンが必要です。

CREATE OR REPLACE MATERIALIZED VIEW [db.]name [ON CLUSTER cluster]
[TO [db.]target_table]
[ENGINE = engine]
[POPULATE]
[REFRESH ...]
AS SELECT ...

主な動作:

  • TO 句なし: 古い内部テーブルは削除され、新しい内部テーブルが作成されます。POPULATE が指定されていない限り、内部テーブル内の既存データは失われます。
  • TO 句あり: 置き換えられるのはビュー定義のみです。ターゲットテーブルとそのデータには影響しません。
  • REFRESHON CLUSTER、およびすべてのエンジンオプションに対応しています。POPULATEAtomic データベースでのみサポートされ、Replicated データベースでは使用できません (以下の POPULATE に関する注記を参照) 。
  • CREATE VIEW 権限と DROP VIEW 権限が必要です。

例:

-- Create a materialized view with an inner table
CREATE OR REPLACE MATERIALIZED VIEW mv
    ENGINE = MergeTree ORDER BY x
    AS SELECT x, sum(y) AS total FROM src GROUP BY x;

-- Replace with a new definition (old inner table data is lost)
CREATE OR REPLACE MATERIALIZED VIEW mv
    ENGINE = MergeTree ORDER BY x
    AS SELECT x, count() AS cnt FROM src GROUP BY x;

-- Replace with POPULATE to backfill from existing source data
CREATE OR REPLACE MATERIALIZED VIEW mv
    ENGINE = MergeTree ORDER BY x
    POPULATE
    AS SELECT x FROM src;

-- Replace an inner-table MV with a TO-table MV (target data is preserved)
CREATE OR REPLACE MATERIALIZED VIEW mv TO target
    AS SELECT x FROM src;

materialized view は、対応する SELECT クエリで変換されたデータを格納します。

TO [db].[table] を指定せずに materialized view を作成する場合は、データを格納するためのテーブルエンジンである ENGINE を指定する必要があります。

TO [db].[table] を指定して materialized view を作成する場合は、POPULATE を使用して既存のソースデータからターゲットテーブルをバックフィルすることもできます (ターゲットテーブルにすでにデータが含まれている場合、バックフィルされた行は追加されます) 。POPULATEREFRESH と併用できません。リフレッシャブルmaterialized view は最初のリフレッシュでデータが補完されるため、POPULATE を使用すると初期データが二重にロードされます (代わりに、最初のリフレッシュをスキップするには EMPTY を使用してください) 。

materialized view は次のように実装されています。SELECT で指定されたテーブルにデータが挿入されると、その挿入データの一部がこの SELECT クエリによって変換され、その結果がビューに挿入されます。

POPULATE を指定すると、既存のソーステーブルのデータは、作成時に VIEW に挿入されます。そうでない場合、VIEW に含まれるのは、VIEW の作成後にソーステーブルに挿入されたデータのみです。

通常の CREATE MATERIALIZED VIEW では、POPULATE はデフォルトで アトミック です (設定 materialized_views_populate_atomically = 1) 。ソーステーブルへの新規挿入に VIEW がサブスクライブされ、既存データのスナップショットが、ソーステーブルに対する短時間の排他ロックのもとで同時に取得されます。これにより、データ投入と同時に挿入されたすべての行は、欠落も重複もなく VIEW に 正確に一度だけ 配信されます。その後、 (長時間実行される可能性がある) データ投入では、ロックを保持せずに固定されたスナップショットを読み取ります。

これはローカルな挿入パスのアトミック性です。排他ロックは、同じサーバー上でこのソーステーブルのストレージロックを取得する挿入とのみ直列化されるため、exactly-once 保証の対象はこのサーバー経由で到着する挿入です。これはクラスター全体の保証ではありません。データ投入と同時に ReplicatedMergeTree ソースの別のレプリカに挿入された行や、分散書き込みパス (たとえば Distributed table への書き込み、または ON CLUSTER 経由) で挿入された行は、この境界の対象外であり、欠落または重複する可能性があります。

データ投入が失敗した場合 (たとえば、負荷の高いソーステーブルに対する排他ロックを lock_acquire_timeout 内に取得できない場合や、実行中に VIEW の SELECT が例外を発生させた場合) 、作成直後の VIEW は削除され、CREATE クエリは失敗します。作成されたものは何も残らないため、単純に再試行できます。TO [db].[table] 形式では、このロールバックで削除されるのは VIEW のみであり、既存のターゲットテーブルが削除されることはありません。ただし、失敗したデータ投入ですでにターゲットに挿入された行は、当該テーブルへの INSERT ... SELECT が失敗した後と同様に残るため、CREATE を再試行すると再度挿入されます。バックフィルを正確に行う必要がある場合は、切り詰めたターゲットテーブルまたは新しいターゲットテーブルに再試行するか、ReplacingMergeTree のような重複排除エンジンを使用してください。

SELECT クエリには DISTINCTGROUP BYORDER BYLIMIT を含めることができます。対応する変換は、挿入されるデータの各ブロックごとに独立して実行される点に注意してください。たとえば、GROUP BY が設定されている場合、データは挿入時に集約されますが、それは挿入データの単一のパケット内でのみ行われます。データがその後さらに集約されることはありません。例外は、SummingMergeTree のように、データ集約を独自に実行する ENGINE を使用する場合です。

materialized VIEW が TO [db.]name 構文を使用している場合は、その VIEW を DETACH し、ターゲットテーブルに対して ALTER を実行したあとで、先ほどデタッチした (DETACH) VIEW を ATTACH できます。

VIEW は通常の table と同じように見えます。たとえば、SHOW TABLES クエリの結果にも表示されます。

VIEW を削除するには、DROP VIEW を使用します。ただし、DROP TABLE も VIEW に対して機能します。

SQL security

DEFINERSQL SECURITY を使うと、ビューの基になるクエリを実行する際に、どの ClickHouse ユーザーを使用するかを指定できます。 SQL SECURITY には、DEFINERINVOKERNONE の 3 つの有効な値があります。DEFINER 句では、既存の任意のユーザー、または CURRENT_USER を指定できます。

次の表は、ビューから読み取るために、どのユーザーにどの権限が必要かを示しています。 なお、SQL security オプションにかかわらず、どの場合でもビューを読み取るには GRANT SELECT ON <view> が引き続き必要です。

SQL security option View Materialized View
DEFINER alice alice は、ビューのソーステーブルに対する SELECT 権限を持っている必要があります。 alice は、ビューのソーステーブルに対する SELECT 権限と、ビューのターゲットテーブルに対する INSERT 権限を持っている必要があります。
INVOKER ユーザーは、ビューのソーステーブルに対する SELECT 権限を持っている必要があります。 materialized views には SQL SECURITY INVOKER を指定できません。
NONE - -

DEFINER/SQL SECURITY が指定されていない場合、結果は ignore_empty_sql_security_in_create_view_query サーバー設定によって異なります。

デフォルト値の true では、クエリは記述どおりに保存され、ビューには空の SQL security タイプが設定されます。通常のビューは呼び出し元の権限で実行され、ターゲットテーブルが明示的に指定された materialized view では、そのターゲットテーブルに対するアクセスチェックがスキップされます。つまり、ソーステーブルへの挿入にはターゲットテーブルに対する INSERT 権限は不要であり、ビューからの読み取りにはそのターゲットテーブルに対する SELECT 権限は不要です。

false では、作成時に次のデフォルト値がビュー定義に書き込まれます。

リフレッシャブルmaterialized view には、設定にかかわらず常にこれらのデフォルト値が設定されます。

ビューは、アタッチ時またはサーバー起動時の再読み込み時に、保存されている定義の SQL security タイプを維持します。そのため、DEFINER/SQL SECURITY なしで保存されたビューは、空の SQL security タイプを維持します。

既存のビューの SQL security を変更するには、次を使用します。

ALTER TABLE MODIFY SQL SECURITY { DEFINER | INVOKER | NONE } [DEFINER = { user | CURRENT_USER }]

CREATE VIEW test_view
DEFINER = alice SQL SECURITY DEFINER
AS SELECT ...
CREATE VIEW test_view
SQL SECURITY INVOKER
AS SELECT ...

Live View

非推奨機能

この機能は非推奨であり、今後削除される予定です。

参考までに、旧ドキュメントはこちらにあります。

リフレッシャブルmaterialized view

CREATE MATERIALIZED VIEW [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
REFRESH [EVERY|AFTER interval [OFFSET interval]]
[RANDOMIZE FOR interval]
[DEPENDS ON [db.]name [, [db.]name [, ...]]]
[SETTINGS name = value [, name = value [, ...]]]
[APPEND]
[TO[db.]name] [(columns)] [ENGINE = engine]
[EMPTY]
[DEFINER = { user | CURRENT_USER }] [SQL SECURITY { DEFINER | NONE }]
AS SELECT ...
[COMMENT 'comment']

ここで、interval は単純なインターバルの列です。

number SECOND|MINUTE|HOUR|DAY|WEEK|MONTH|YEAR

REFRESH clause では、EVERYAFTERDEPENDS ON の少なくとも 1 つを指定する必要があります。これらを 1 つも指定しない単独の REFRESH は拒否されます。EVERY/AFTER を伴わない REFRESH DEPENDS ON ... は、REFRESH AFTER 0 SECOND DEPENDS ON ... の省略形です。詳細は以下の Refresh Dependencies を参照してください。

対応するクエリを定期的に実行し、その結果をテーブルに格納します。

  • APPEND が指定されている場合、各リフレッシュでは既存の行を削除せずにテーブルへ行を挿入します。この insert は、通常の INSERT INTO ... SELECT クエリと同様にアトミックではありません。
  • それ以外の場合、各リフレッシュでテーブルの以前の内容はアトミックに置き換えられます。

通常の、リフレッシュ機能のない materialized view との違い:

  • insert trigger はありません。SELECT で指定したテーブルに新しいデータが挿入されても、そのデータがリフレッシャブルmaterialized view に自動的に反映されることは ありません。代わりに、データの挿入は定期実行または手動実行のリフレッシュ時にのみ行われます。
  • SELECT クエリには制約がありません。テーブル関数 (例: url()) 、ビュー、UNION、JOIN はいずれも使用できます。

リフレッシュ スケジュール

リフレッシュ スケジュールの例:

REFRESH EVERY 1 DAY -- every day, at midnight (UTC)
REFRESH EVERY 1 MONTH -- on 1st day of every month, at midnight
REFRESH EVERY 1 MONTH OFFSET 5 DAY 2 HOUR -- on 6th day of every month, at 2:00 am
REFRESH EVERY 2 WEEK OFFSET 5 DAY 15 HOUR 10 MINUTE -- every other Saturday, at 3:10 pm
REFRESH EVERY 30 MINUTE -- at 00:00, 00:30, 01:00, 01:30, etc
REFRESH AFTER 30 MINUTE -- 30 minutes after the previous refresh completes, no alignment with time of day
-- REFRESH AFTER 1 HOUR OFFSET 1 MINUTE -- syntax error, OFFSET is not allowed with AFTER
REFRESH EVERY 1 WEEK 2 DAYS -- every 9 days, not on any particular day of the week or month;
                            -- specifically, when day number (since 1969-12-29) is divisible by 9
REFRESH EVERY 5 MONTHS -- every 5 months, different months each year (as 12 is not divisible by 5);
                       -- specifically, when month number (since 1970-01) is divisible by 5

RANDOMIZE FOR は、各リフレッシュの時刻をランダムに調整します。例:

REFRESH EVERY 1 DAY OFFSET 2 HOUR RANDOMIZE FOR 1 HOUR -- every day at random time between 01:30 and 02:30

特定のビューでは、同時に実行できる refresh は最大 1 つまでです。たとえば、REFRESH EVERY 1 MINUTE を設定したビューの refresh に 2 分かかる場合、実際の refresh 間隔は 2 分になります。その後、処理が速くなって 10 秒で refresh できるようになれば、再び 1 分ごとの refresh に戻ります。 (つまり、実行されなかった refresh の遅れを取り戻すために 10 秒ごとに refresh されることはありません。そのような未実行分の backlog は存在しません。)

通常、最初の refresh は materialized view の作成直後に開始されます。前回の refresh からの経過時間は無限大であるため、どのスケジュールでも「今すぐ refresh すべき時刻」と判断されるからです。EMPTY が指定されている場合、この初回の refresh はスキップされ、最初の refresh は次にスケジュールされた時刻に実行されます。たとえば EVERY 1 HOUR では、最初の refresh は現在の時刻のちょうど1時間の区切りで実行されます。

Replicated DB 内で

リフレッシャブルmaterialized view が Replicated database 内にある場合、各レプリカは相互に協調し、スケジュールされた時刻ごとに 1 つのレプリカだけがリフレッシュを実行します。リフレッシュで生成されたデータをすべてのレプリカが参照できるようにするため、ReplicatedMergeTree テーブルエンジンが必要です。

APPEND モードでは、SETTINGS all_replicas = 1 を使用して協調を無効にできます。これにより、各レプリカは互いに独立してリフレッシュを実行します。この場合、ReplicatedMergeTree は必須ではありません。

APPEND 以外のモードでは、協調されたリフレッシュのみがサポートされます。協調なしで行うには、Atomic データベースと CREATE ... ON CLUSTER クエリを使用して、すべてのレプリカにリフレッシャブルmaterialized view を作成してください。

協調は Keeper を通じて行われます。znode path は default_replica_path サーバー設定によって決まります。

リフレッシュの依存関係

DEPENDS ON は、異なるテーブルのリフレッシュを同期します:

CREATE MATERIALIZED VIEW dependent REFRESH EVERY 1 HOUR DEPENDS ON dependency [...]

依存ビューのリフレッシュは、依存先のすべてのビューのリフレッシュが完了してから開始されます。

別のビューのリフレッシュ直後にリフレッシュするには:

CREATE MATERIALIZED VIEW dependent REFRESH AFTER 0 SECOND DEPENDS ON dependency [...]

あるいは、次のようにも書けます:

CREATE MATERIALIZED VIEW dependent REFRESH DEPENDS ON dependency [...]

伝播レイテンシを一定に保つための DEPENDS ON の使用

両方のビューが同じ周期で REFRESH EVERY を使用している場合、依存関係は各時間スロットに適用されます。

たとえば、ビュー X と Y がどちらも REFRESH EVERY 1 HOUR を使用し、Y が X の出力テーブルを読み取るとします。依存関係がない場合、Y は通常、前の時間のリフレッシュで生成された X のデータを参照します。DEPENDS ON X を使用すると、Y の 11:00 のリフレッシュは、X の 11:00 のリフレッシュが完了した後にのみ開始されます。

           10:00            11:00            12:00
           │                │                │
  X:        [run]┐           [run]┐           [run]┐
                 │                │                │
  Y:             └►[run]          └►[run]          └►[run]

依存対象と依存側は、リフレッシュの実行時間がリフレッシュ間隔より長い場合、それぞれ独立してタイムスロットをスキップすることがあります。依存側が依存対象の各リフレッシュに対してちょうど1回ずつリフレッシュされる保証はありません。

           10:00          11:00          12:00          13:00
           │              │              │              |
  X:        [run]┐         [run]┐         [run]┐         [run]┐
                 │              └────┐    (Y skips 12:00)     └───┐
  Y:             └►[10:00 ru------un]└►[11:00 ru---------------un]└►[13:00 run]

バッチ単位のストリーム処理での DEPENDS ON の使用

REFRESH EVERY を使用しない場合、依存するビュー X は、X の前回のリフレッシュ以降に、そのすべての依存関係が少なくとも 1 回リフレッシュされていればリフレッシュされます。REFRESH AFTER T は遅延を追加するもので、依存先は依存元のリフレッシュ完了から T 時間後にリフレッシュを開始します。

循環依存は許可されており、有用でもあります。次のリフレッシャブルmaterialized view のグラフを考えてみましょう。

  1. X はある stream から行の batch を取り出し、それをテーブルに格納します。
  2. 次に、Y と Z はどちらもそのテーブルを読み取り、異なる集計を行って、結果を別のテーブルに追記します。
  3. batch の処理が完了すると、X は次の batch を取り出し、この cycle が繰り返されます。
            source


          ┌─────────┐
     ┌───►│    X    │◄───┐
     │    └──┬───┬──┘    │
  DEPENDS    │   │    DEPENDS
    ON       ▼   ▼      ON
     │      ┌─┐ ┌─┐      │
     └──────┤Y│ │Z├──────┘
            └─┘ └─┘

全体の例:

CREATE TABLE current_batch (t UInt64, v Int64) ENGINE ReplicatedMergeTree ORDER BY t;
CREATE TABLE batch_log (max_t UInt64, n Int64, v_sum Int64, processed_at DateTime64) ENGINE ReplicatedMergeTree ORDER BY max_t;
CREATE TABLE stats (h UInt64, n UInt64) ENGINE ReplicatedSummingMergeTree ORDER BY h;

-- (system.numbers stands in for a data source with monotonically increasing timestamps or sequence numbers)
CREATE MATERIALIZED VIEW current_batch_v REFRESH EVERY 10 SECOND DEPENDS ON batch_log_v, stats_v TO current_batch AS SELECT number as t, number * 10 as v FROM system.numbers WHERE number > (SELECT max(max_t) FROM batch_log) LIMIT 100;

CREATE MATERIALIZED VIEW batch_log_v REFRESH DEPENDS ON current_batch_v APPEND TO batch_log AS SELECT max(t) as max_t, count() as n, sum(v) as v_sum, now64() as processed_at FROM current_batch;

CREATE MATERIALIZED VIEW stats_v REFRESH DEPENDS ON current_batch_v APPEND TO stats AS SELECT cityHash64(v) % 20 as h, count() as n FROM current_batch GROUP BY h;

-- Must trigger initial refresh manually.
SYSTEM REFRESH VIEW current_batch_v;

より長いチェーンでも機能します。

これは、リフレッシュの協調が有効になっている場合、つまりビューが Replicated または Shared データベースにある場合にのみ、うまく機能します。協調がないと、サーバーの再起動でその連鎖が途切れるため、ビューの作成後に一度だけではなく、再起動のたびに手動で SYSTEM REFRESH VIEW を実行する必要があります。

リフレッシュ設定

利用可能なリフレッシュ設定:

  • refresh_retries - リフレッシュクエリが例外で失敗した場合に、再試行する回数を指定します。すべての再試行が失敗した場合は、次にスケジュールされているリフレッシュ時刻までスキップします。0 は再試行なし、-1 は無制限の再試行を意味します。デフォルト: 2。
  • refresh_retry_initial_backoff_ms - refresh_retries が 0 でない場合の、最初の再試行までの待機時間です。以降の再試行では、待機時間が回ごとに 2 倍になり、最大で refresh_retry_max_backoff_ms まで増加します。デフォルト: 100 ms。
  • refresh_retry_max_backoff_ms - リフレッシュ試行の間隔が指数的に増加する際の上限です。デフォルト: 60000 ms (1 分) 。
  • all_replicas - APPEND を使用する Replicated database で、すべてのレプリカが独立してリフレッシュするか、スケジュール時刻ごとに 1 つのレプリカだけがリフレッシュするかを制御します。ビューの作成後は変更できません。デフォルト: false

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

既存のリフレッシャブルmaterialized viewのリフレッシュパラメータは、ALTER TABLE ... MODIFY REFRESHを使って変更します。

ALTER TABLE [db.]name MODIFY REFRESH EVERY|AFTER ... [RANDOMIZE FOR ...] [DEPENDS ON ...] [SETTINGS ...]

スケジュール (EVERY または AFTER) の指定は必須です。このステートメントでは、リフレッシュに関するすべてのパラメーター (スケジュール、RANDOMIZE FORDEPENDS ON、およびリフレッシュ設定) が、指定した内容で常に丸ごと置き換えられます。省略した項目は、設定であればデフォルト値に戻され、依存関係やランダム化であれば削除されます。

例:

-- スケジュールを変更し、既存の設定と依存関係を削除する。
ALTER TABLE rmv MODIFY REFRESH EVERY 30 MINUTE;

-- スケジュールを変更し、再試行の動作を調整する。
ALTER TABLE rmv MODIFY REFRESH EVERY 30 MINUTE
SETTINGS refresh_retries = 5,
         refresh_retry_initial_backoff_ms = 500,
         refresh_retry_max_backoff_ms = 60000;

-- 期間を変更しながら依存関係を維持する。
ALTER TABLE rmv MODIFY REFRESH EVERY 6 HOUR DEPENDS ON other_rmv;

-- `DEPENDS ON` を省略して依存関係を削除する。
ALTER TABLE rmv MODIFY REFRESH EVERY 6 HOUR;

その他の操作

すべてのリフレッシャブルmaterialized viewの状態は、テーブルsystem.view_refreshesで確認できます。特に、リフレッシュの進行状況 (実行中の場合) 、前回および次回のリフレッシュ時刻、リフレッシュが失敗した場合の例外メッセージが含まれます。

手動でリフレッシュを停止、開始、トリガー、またはキャンセルするには、SYSTEM STOP|START|REFRESH|WAIT|CANCEL VIEWを使用します。

リフレッシュの完了を待機するには、SYSTEM WAIT VIEWを使用します。特に、ビューの作成後に初回リフレッシュが完了するのを待つ際に便利です。

ウィンドウビュー

非推奨機能
ClickHouse Cloud ではサポートされていません
CREATE WINDOW VIEW [IF NOT EXISTS] [db.]table_name [TO [db.]table_name] [INNER ENGINE engine] [ENGINE engine] [WATERMARK strategy] [ALLOWED_LATENESS interval_function] [POPULATE]
AS SELECT ...
GROUP BY time_window_function
[COMMENT 'comment']

ウィンドウビュー は、時間ウィンドウ単位でデータを集約し、ウィンドウの発火準備が整うと結果を出力できます。部分的な集約結果は、レイテンシを低減するために内部テーブル (または指定したテーブル) に保存され、処理結果を指定したテーブルに送信したり、WATCH クエリを使用して通知を送信したりできます。

ウィンドウビュー の作成は、MATERIALIZED VIEW の作成に似ています。ウィンドウビュー では、中間データを保存するための内部ストレージエンジンが必要です。内部ストレージは INNER ENGINE 句を使用して指定でき、ウィンドウビュー はデフォルトの内部エンジンとして AggregatingMergeTree を使用します。

TO [db].[table] なしで ウィンドウビュー を作成する場合は、データを保存するためのテーブルエンジンである ENGINE を指定する必要があります。

時間ウィンドウ関数

時間ウィンドウ関数は、レコードのウィンドウ境界の下限と上限を取得するために使用します。ウィンドウビュー は時間ウィンドウ関数と併せて使用する必要があります。

時間属性

ウィンドウビューは、処理時間イベント時間の両方をサポートしています。

処理時間では、ウィンドウビューはローカルマシンの時刻に基づいて結果を生成し、デフォルトで使用されます。これは最もシンプルで分かりやすい時間の概念ですが、決定性は保証されません。処理時間属性は、時間ウィンドウ関数の time_attr にテーブルのカラムを指定するか、now() 関数を使用することで定義できます。次のクエリは、処理時間を使用するウィンドウビューを作成します。

CREATE WINDOW VIEW wv AS SELECT count(number), tumbleStart(w_id) as w_start from date GROUP BY tumble(now(), INTERVAL '5' SECOND) as w_id

イベント時刻 とは、各イベントが生成元のデバイスで実際に発生した時刻のことです。この時刻は通常、レコードの生成時にレコード内へ埋め込まれます。イベント時刻処理を行うことで、イベントの順序が前後していたり、到着が遅れたりする場合でも、一貫した結果を得られます。ウィンドウビュー は、WATERMARK 構文を使用してイベント時刻処理をサポートします。

ウィンドウビュー には、3 つのウォーターマーク戦略があります。

  • STRICTLY_ASCENDING: これまでに観測された最大のタイムスタンプのウォーターマークを出力します。タイムスタンプが最大タイムスタンプより小さい行は、遅延データとは見なされません。
  • ASCENDING: これまでに観測された最大のタイムスタンプから 1 を引いたウォーターマークを出力します。タイムスタンプが最大タイムスタンプ以下の行は、遅延データとは見なされません。
  • BOUNDED: WATERMARK=INTERVAL。これまでに観測された最大のタイムスタンプから指定した遅延を引いたウォーターマークを出力します。

以下のクエリは、WATERMARK を使用して ウィンドウビュー を作成する例です。

CREATE WINDOW VIEW wv WATERMARK=STRICTLY_ASCENDING AS SELECT count(number) FROM date GROUP BY tumble(timestamp, INTERVAL '5' SECOND);
CREATE WINDOW VIEW wv WATERMARK=ASCENDING AS SELECT count(number) FROM date GROUP BY tumble(timestamp, INTERVAL '5' SECOND);
CREATE WINDOW VIEW wv WATERMARK=INTERVAL '3' SECOND AS SELECT count(number) FROM date GROUP BY tumble(timestamp, INTERVAL '5' SECOND);

デフォルトでは、ウォーターマークに達するとウィンドウが発火し、ウォーターマークより遅れて到着した要素は破棄されます。ウィンドウビュー では、ALLOWED_LATENESS=INTERVAL を設定することで遅延イベントの処理をサポートします。遅延処理の例を次に示します。

CREATE WINDOW VIEW test.wv TO test.dst WATERMARK=ASCENDING ALLOWED_LATENESS=INTERVAL '2' SECOND AS SELECT count(a) AS count, tumbleEnd(wid) AS w_end FROM test.mt GROUP BY tumble(timestamp, INTERVAL '5' SECOND) AS wid;

遅延発火によって出力される要素は、以前の計算結果に対する更新結果として扱う必要があることに注意してください。ウィンドウビュー は、window の終了時に発火するのではなく、遅延イベントが到着すると即座に発火します。そのため、同じ window に対して複数の出力が生成されます。ユーザーは、これらの重複した結果を考慮するか、重複排除する必要があります。

ウィンドウビュー で指定した SELECT クエリは、ALTER TABLE ... MODIFY QUERY ステートメントを使用して変更できます。新しい SELECT クエリによって得られるデータ構造は、TO [db.]name 句の有無にかかわらず、元の SELECT クエリと同じである必要があります。中間状態は再利用できないため、現在の window 内のデータは失われることに注意してください。

新しいウィンドウの監視

ウィンドウビュー は、変更を監視するための WATCH クエリをサポートしているほか、TO 構文を使用して結果をテーブルに出力することもできます。

WATCH [db.]window_view
[EVENTS]
[LIMIT n]
[FORMAT format]

クエリを終了するまでに受け取る更新回数は、LIMIT で指定できます。EVENTS 句を使うと、WATCH クエリの簡易形式を利用できます。この場合、クエリ結果の代わりに、最新のクエリのウォーターマークだけが返されます。

設定

  • window_view_clean_interval: 古くなったデータを解放するための、ウィンドウビュー のクリーンアップ間隔 (秒) です。システム時刻または WATERMARK 設定に基づき、まだ完全にはトリガーされていないウィンドウは保持され、それ以外のデータは削除されます。
  • window_view_heartbeat_interval: watchクエリが動作中であることを示すハートビート間隔 (秒) です。
  • wait_for_window_view_fire_signal_timeout: イベント時刻処理において、ウィンドウビュー の fire signal を待機する際のタイムアウトです。

data という名前のログテーブルで、10秒ごとのクリックログ数を集計する必要があるとします。テーブル構造は次のとおりです。

CREATE TABLE data ( `id` UInt64, `timestamp` DateTime) ENGINE = Memory;

まず、10秒間隔のタンブルウィンドウを持つウィンドウビューを作成します。

CREATE WINDOW VIEW wv as select count(id), tumbleStart(w_id) as window_start from data group by tumble(timestamp, INTERVAL '10' SECOND) as w_id

次に、結果を取得するには WATCH クエリを使用します。

WATCH wv

data テーブルにログが挿入されると、

INSERT INTO data VALUES(1,now())

WATCH クエリの結果は次のように表示されます:

┌─count(id)─┬────────window_start─┐
│         1 │ 2020-01-14 16:56:40 │
└───────────┴─────────────────────┘

あるいは、TO構文を使用して、出力を別のテーブルに関連付けることもできます。

CREATE WINDOW VIEW wv TO dst AS SELECT count(id), tumbleStart(w_id) as window_start FROM data GROUP BY tumble(timestamp, INTERVAL '10' SECOND) as w_id

その他の例は、ClickHouse のステートフルテスト内にあります (そこでは *window_view* という名前です) 。

ウィンドウビュー の使用法

ウィンドウビュー は、次のようなシナリオで役立ちます。

  • 監視: ログのメトリクスを時間ごとに集計・計算し、その結果をターゲットテーブルに出力します。ダッシュボードでは、ターゲットテーブルをソーステーブルとして使用できます。
  • 分析: 時間ウィンドウ内のデータを自動的に集計して前処理します。これは、大量のログを分析する場合に役立ちます。前処理により、複数のクエリで同じ計算を繰り返す必要がなくなり、クエリのレイテンシが低減されます。

一時ビュー

ClickHouse は、一時ビューをサポートしており、次のような特性があります (該当する場合は一時テーブルと同様です) 。

  • セッション存続期間 一時ビューは現在のセッション中にのみ存在します。セッションが終了すると自動的に削除されます。

  • データベースなし 一時ビューをデータベース名で修飾することはできません。一時ビューはデータベースの外側 (セッションのネームスペース) に存在します。

  • レプリケートされない / ON CLUSTER なし 一時オブジェクトはセッションローカルであり、ON CLUSTER を付けて作成することはできません

  • 名前解決 一時オブジェクト (テーブルまたはビュー) が永続オブジェクトと同じ名前を持ち、クエリがデータベース名を付けずにその名前を参照した場合は、一時オブジェクトが使用されます。

  • 論理オブジェクト (ストレージなし) 一時ビューが保持するのは SELECT テキストのみです (内部的には View ストレージを使用します) 。データは永続化されず、INSERT も受け付けません。

  • Engine 句 ENGINE を指定する必要はありませんENGINE = View として指定した場合も、無視されるか、同じ論理ビューとして扱われます。

  • セキュリティ / 権限 一時ビューを作成するには CREATE TEMPORARY VIEW 権限が必要です。この権限は CREATE VIEW によって暗黙的に付与されます。

  • SHOW CREATE 一時ビューの DDL を表示するには、SHOW CREATE TEMPORARY VIEW view_name; を使用します。

構文

CREATE TEMPORARY VIEW [IF NOT EXISTS] view_name AS <select_query>

OR REPLACE は、一時テーブルとの整合性を保つため、一時ビューでは サポートされていません。一時ビューを「置き換える」必要がある場合は、いったん削除してから再作成してください。

一時ソーステーブルを作成し、その上に一時ビューを作成します。

CREATE TEMPORARY TABLE t_src (id UInt32, val String);
INSERT INTO t_src VALUES (1, 'a'), (2, 'b');

CREATE TEMPORARY VIEW tview AS
SELECT id, upper(val) AS u
FROM t_src
WHERE id <= 2;

SELECT * FROM tview ORDER BY id;

DDLを表示します:

SHOW CREATE TEMPORARY VIEW tview;

削除:

DROP TEMPORARY VIEW IF EXISTS tview;  -- 一時ビューは TEMPORARY TABLE 構文でドロップします

使用不可 / 制限事項

  • CREATE OR REPLACE TEMPORARY VIEW ...使用できません (DROP + CREATE を使用してください) 。
  • CREATE TEMPORARY MATERIALIZED VIEW ... / WINDOW VIEW使用できません
  • CREATE TEMPORARY VIEW db.view AS ...使用できません (データベース修飾子は指定できません) 。
  • CREATE TEMPORARY VIEW view ON CLUSTER 'name' AS ...使用できません (一時オブジェクトはセッションローカルです) 。
  • POPULATE, REFRESH, TO [db.table], 内部エンジン、および MV 固有の句は、一時ビューには 適用されません

分散クエリに関する注意事項

一時ビューは単なる定義にすぎず、受け渡すデータはありません。一時ビューが一時テーブル (たとえば Memory) を参照している場合、そのデータは一時テーブルと同様に、分散クエリの実行中にリモートサーバーへ転送されることがあります。

-- A session-scoped, in-memory table
CREATE TEMPORARY TABLE temp_ids (id UInt64) ENGINE = Memory;

INSERT INTO temp_ids VALUES (1), (5), (42);

-- A session-scoped view over the temp table (purely logical)
CREATE TEMPORARY VIEW v_ids AS
SELECT id FROM temp_ids;

-- Replace 'test' with your cluster name.
-- GLOBAL JOIN forces ClickHouse to *ship* the small join-side (temp_ids via v_ids)
-- to every remote server that executes the left side.
SELECT count()
FROM cluster('test', system.numbers) AS n
GLOBAL ANY INNER JOIN v_ids USING (id)
WHERE n.number < 100;
Navigation