메인 콘텐츠로 건너뛰기
새로운 뷰를 생성합니다. 뷰는 일반, materialized, 갱신 가능한 materialized, 윈도우 유형으로 만들 수 있습니다.

일반 뷰

구문:
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 ...)

매개변수화된 뷰(Parameterized View)

매개변수화된 뷰는 일반 뷰와 유사하지만, 즉시 해석되지 않는 매개변수를 포함해 생성할 수 있습니다. 이러한 뷰는 테이블 함수와 함께 사용할 수 있으며, 이 경우 함수 이름에는 뷰 이름을 지정하고 인수에는 매개변수 값을 지정합니다.
CREATE VIEW view AS SELECT * FROM TABLE WHERE Column1={column1:datatype1} and Column2={column2:datatype2} ...
위 구문은 아래와 같이 매개변수를 치환하여 테이블 함수로 사용할 수 있는 테이블용 뷰를 생성합니다.
SELECT * FROM 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 절 사용 시: 뷰 정의만 대체되며 대상 테이블과 그 데이터에는 영향이 없습니다.
  • REFRESH, ON CLUSTER, 그리고 모든 엔진 옵션과 호환됩니다. POPULATEAtomic 데이터베이스에서만 지원되며, Replicated 데이터베이스에서는 허용되지 않습니다(아래 POPULATE 참고 사항 참조).
  • CREATE VIEWDROP VIEW 권한이 필요합니다.
CREATE OR REPLACE MATERIALIZED VIEWAtomic 또는 Replicated 데이터베이스 엔진에서만 지원됩니다. Ordinary 데이터베이스 엔진에서는 지원되지 않습니다.
예시:
-- 내부 테이블을 사용하여 materialized view 생성
CREATE OR REPLACE MATERIALIZED VIEW mv
    ENGINE = MergeTree ORDER BY x
    AS SELECT x, sum(y) AS total FROM src GROUP BY x;

-- 새 정의로 교체 (기존 내부 테이블 데이터는 손실됨)
CREATE OR REPLACE MATERIALIZED VIEW mv
    ENGINE = MergeTree ORDER BY x
    AS SELECT x, count() AS cnt FROM src GROUP BY x;

-- POPULATE을 사용하여 기존 소스 데이터로 백필(backfill)
CREATE OR REPLACE MATERIALIZED VIEW mv
    ENGINE = MergeTree ORDER BY x
    POPULATE
    AS SELECT x FROM src;

-- 내부 테이블 MV를 TO 테이블 MV로 교체 (대상 테이블 데이터는 유지됨)
CREATE OR REPLACE MATERIALIZED VIEW mv TO target
    AS SELECT x FROM src;
Materialized views를 사용하는 단계별 가이드는 여기에서 확인할 수 있습니다.
materialized view는 해당 SELECT 쿼리로 변환된 데이터를 저장합니다. TO [db].[table] 없이 materialized view를 생성할 때는 데이터를 저장할 테이블 엔진인 ENGINE을 지정해야 합니다. TO [db].[table]과 함께 materialized view를 생성할 때는 POPULATE를 함께 사용할 수 없습니다. materialized view는 다음과 같이 동작합니다. SELECT에 지정된 테이블에 데이터를 삽입하면, 삽입된 데이터의 일부가 이 SELECT 쿼리로 변환되고 그 결과가 뷰에 삽입됩니다.
ClickHouse의 materialized view는 대상 테이블에 삽입할 때 컬럼 순서가 아니라 column names를 사용합니다. SELECT 쿼리 결과에 일부 컬럼 이름이 없으면, 해당 컬럼이 널 허용이 아니더라도 ClickHouse는 기본값을 사용합니다. materialized view를 사용할 때는 모든 컬럼에 alias를 추가하는 것이 안전합니다.ClickHouse의 materialized view는 삽입 트리거에 더 가깝게 구현되어 있습니다. 뷰 쿼리에 집계가 포함된 경우, 이는 새로 삽입된 데이터의 batch에만 적용됩니다. source 테이블의 기존 데이터가 변경되어도(update, delete, drop partition 등) materialized view에는 반영되지 않습니다.ClickHouse의 materialized view는 오류 발생 시 deterministic한 동작을 보장하지 않습니다. 즉, 이미 기록된 block은 대상 테이블에 유지되지만, 오류 발생 이후의 모든 block은 기록되지 않습니다.기본적으로 여러 뷰 중 하나로 push하는 데 실패하면 INSERT 쿼리도 함께 실패하며, 일부 block은 대상 테이블에 기록되지 않을 수 있습니다. 이 동작은 materialized_views_ignore_errors 설정으로 변경할 수 있습니다(INSERT 쿼리에 설정해야 합니다). materialized_views_ignore_errors=true로 설정하면 뷰로 push하는 동안 발생하는 모든 오류가 무시되고, 모든 block이 대상 테이블에 기록됩니다.또한 system.*_log 테이블에서는 기본적으로 materialized_views_ignore_errorstrue로 설정되어 있다는 점도 유의하십시오.
POPULATE를 지정하면, 기존 테이블 데이터가 CREATE TABLE ... AS SELECT ...를 수행한 것처럼 생성 시 뷰에 삽입됩니다. 그렇지 않으면 쿼리에는 뷰를 생성한 후 테이블에 삽입된 데이터만 포함됩니다. 뷰를 생성하는 동안 테이블에 삽입된 데이터는 뷰에 삽입되지 않으므로, POPULATE 사용은 권장하지 않습니다.
POPULATECREATE TABLE ... AS SELECT ...처럼 동작하므로 다음과 같은 제한이 있습니다.
  • 복제된 데이터베이스에서는 지원되지 않습니다
  • ClickHouse Cloud에서는 지원되지 않습니다
대신 별도의 INSERT ... SELECT를 사용할 수 있습니다.
SELECT 쿼리에는 DISTINCT, GROUP BY, ORDER BY, LIMIT를 포함할 수 있습니다. 해당 변환은 삽입된 데이터의 각 블록에서 서로 독립적으로 수행된다는 점에 유의하십시오. 예를 들어 GROUP BY가 설정된 경우 데이터는 삽입 중에 집계되지만, 이는 삽입된 데이터의 단일 패킷 내에서만 수행됩니다. 이후에는 데이터가 추가로 집계되지 않습니다. 예외적으로 SummingMergeTree처럼 자체적으로 데이터 집계를 수행하는 ENGINE을 사용할 때는 다릅니다. materialized view가 TO [db.]name 구문을 사용하는 경우, 뷰를 DETACH하고 대상 테이블에 ALTER를 실행한 다음, 앞서 분리한(DETACH) 뷰를 ATTACH할 수 있습니다. materialized view는 optimize_on_insert 설정의 영향을 받는다는 점에 유의하십시오. 데이터는 뷰에 삽입되기 전에 머지됩니다. 뷰는 일반 테이블과 동일하게 표시됩니다. 예를 들어 SHOW TABLES 쿼리 결과에 나열됩니다. 뷰를 삭제하려면 DROP VIEW를 사용하십시오. DROP TABLE도 VIEW에 사용할 수 있습니다.

SQL 보안

DEFINERSQL SECURITY를 사용하면 뷰의 기반 쿼리를 실행할 때 어떤 ClickHouse 사용자를 사용할지 지정할 수 있습니다. SQL SECURITY에는 DEFINER, INVOKER, NONE의 세 가지 유효한 값이 있습니다. DEFINER 절에는 기존 사용자 또는 CURRENT_USER를 지정할 수 있습니다. 다음 표는 뷰에서 SELECT할 때 어떤 사용자에게 어떤 권한이 필요한지 설명합니다. SQL 보안 옵션과 관계없이, 어떤 경우에도 뷰를 읽으려면 여전히 GRANT SELECT ON <view> 권한이 필요합니다.
SQL security optionViewMaterialized View
DEFINER alicealice는 뷰의 소스 테이블에 대한 SELECT 권한이 있어야 합니다.alice는 뷰의 소스 테이블에 대한 SELECT 권한과 뷰의 대상 테이블에 대한 INSERT 권한이 있어야 합니다.
INVOKER사용자는 뷰의 소스 테이블에 대한 SELECT 권한이 있어야 합니다.materialized view에는 SQL SECURITY INVOKER를 지정할 수 없습니다.
NONE--
SQL SECURITY NONE은 더 이상 권장되지 않는 옵션입니다. SQL SECURITY NONE으로 뷰를 생성할 수 있는 권한이 있는 사용자는 임의의 쿼리를 실행할 수 있습니다. 따라서 이 옵션으로 뷰를 생성하려면 GRANT ALLOW SQL SECURITY NONE TO <user> 권한이 필요합니다.
DEFINER/SQL SECURITY를 지정하지 않으면 기본값이 사용됩니다: DEFINER/SQL SECURITY를 지정하지 않고 뷰를 ATTACH하면, materialized view의 기본값은 SQL SECURITY NONE이고 일반 뷰의 기본값은 SQL SECURITY INVOKER입니다. 기존 뷰의 SQL 보안을 변경하려면 다음을 사용합니다:
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 ...

라이브 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
해당 쿼리를 주기적으로 실행하고 결과를 테이블에 저장합니다.
  • APPEND를 지정하면 각 갱신 시 기존 행을 삭제하지 않고 테이블에 행을 삽입합니다. 이 삽입은 일반적인 INSERT INTO ... SELECT 쿼리와 마찬가지로 원자적이지 않습니다.
  • 그렇지 않으면 각 갱신 시 테이블의 이전 내용이 원자적으로 대체됩니다.
일반적인 비갱신형 materialized view와의 차이점:
  • 삽입 트리거가 없습니다. SELECT에 지정된 테이블에 새 데이터가 삽입되어도 갱신 가능 materialized view로 자동 반영되지 않습니다. 대신 데이터 삽입은 주기적 또는 수동 갱신 실행 중에만 이루어집니다.
  • SELECT 쿼리에는 제한이 없습니다. 테이블 함수(예: url()), 뷰, UNION, JOIN을 모두 사용할 수 있습니다.
쿼리의 REFRESH ... SETTINGS 부분에 있는 설정은 갱신 설정(예: refresh_retries)이며, 일반 설정(예: max_threads)과는 구분됩니다. 일반 설정은 쿼리 끝에 SETTINGS를 사용해 지정할 수 있습니다.

갱신 일정

예시 갱신 일정:
REFRESH EVERY 1 DAY -- 매일 자정(UTC)
REFRESH EVERY 1 MONTH -- 매월 1일 자정
REFRESH EVERY 1 MONTH OFFSET 5 DAY 2 HOUR -- 매월 6일 오전 2:00
REFRESH EVERY 2 WEEK OFFSET 5 DAY 15 HOUR 10 MINUTE -- 격주 토요일 오후 3:10
REFRESH EVERY 30 MINUTE -- 00:00, 00:30, 01:00, 01:30 등
REFRESH AFTER 30 MINUTE -- 이전 갱신 완료 후 30분, 시각 기준 정렬 없음
-- REFRESH AFTER 1 HOUR OFFSET 1 MINUTE -- 구문 오류, AFTER에는 OFFSET 사용 불가
REFRESH EVERY 1 WEEK 2 DAYS -- 9일마다, 특정 요일이나 날짜에 고정되지 않음;
                            -- 구체적으로, 1969-12-29 이후 경과 일수가 9로 나누어 떨어질 때
REFRESH EVERY 5 MONTHS -- 5개월마다, 매년 다른 월에 실행됨(12는 5로 나누어 떨어지지 않으므로);
                       -- 구체적으로, 1970-01 이후 경과 월 번호가 5로 나누어 떨어질 때
RANDOMIZE FOR는 각 갱신 시각을 무작위로 조정합니다. 예:
REFRESH EVERY 1 DAY OFFSET 2 HOUR RANDOMIZE FOR 1 HOUR -- 매일 01:30에서 02:30 사이의 임의 시간에 실행
지정된 뷰에서는 한 번에 최대 하나의 갱신만 실행될 수 있습니다. 예를 들어 REFRESH EVERY 1 MINUTE가 설정된 뷰의 갱신에 2분이 걸리면, 실제로는 2분마다 갱신됩니다. 이후 속도가 빨라져 10초 만에 갱신을 마치게 되면, 다시 1분마다 갱신됩니다. (즉, 누락된 갱신을 따라잡기 위해 10초마다 갱신되지는 않습니다. 그런 식의 밀린 갱신은 존재하지 않습니다.) 또한 CREATE 쿼리에서 EMPTY를 지정하지 않는 한, materialized view가 생성된 직후 즉시 갱신이 시작됩니다. EMPTY를 지정하면 첫 번째 갱신은 일정에 따라 수행됩니다.

복제된 DB에서

갱신 가능 구체화 뷰가 복제된 데이터베이스에 있는 경우, 레플리카는 서로 coordination하여 예약된 각 시점마다 하나의 레플리카만 갱신을 수행합니다. 갱신으로 생성된 데이터를 모든 레플리카가 확인할 수 있도록 ReplicatedMergeTree 테이블 엔진이 필요합니다. APPEND 모드에서는 SETTINGS all_replicas = 1을 사용해 coordination을 비활성화할 수 있습니다. 이렇게 하면 각 레플리카가 서로 독립적으로 갱신을 수행합니다. 이 경우 ReplicatedMergeTree는 필요하지 않습니다. APPEND가 아닌 모드에서는 coordination된 갱신만 지원됩니다. coordination되지 않은 방식을 사용하려면 Atomic 데이터베이스와 CREATE ... ON CLUSTER 쿼리를 사용해 모든 레플리카에 갱신 가능 구체화 뷰를 생성하십시오. coordination은 Keeper를 통해 수행됩니다. znode 경로는 default_replica_path 서버 설정으로 결정됩니다.

갱신 종속성

DEPENDS ON은 서로 다른 테이블의 갱신을 동기화합니다. 예를 들어, 2개의 갱신 가능 materialized view로 이루어진 체인이 있다고 가정하겠습니다.
CREATE MATERIALIZED VIEW source REFRESH EVERY 1 DAY AS SELECT * FROM url(...)
CREATE MATERIALIZED VIEW destination REFRESH EVERY 1 DAY AS SELECT ... FROM source
DEPENDS ON이 없으면 두 뷰 모두 자정에 갱신을 시작하므로, 일반적으로 destination에서는 source에 있는 전날 데이터가 표시됩니다. 종속성을 추가하면:
CREATE MATERIALIZED VIEW destination REFRESH EVERY 1 DAY DEPENDS ON source AS SELECT ... FROM source
그러면 해당 날짜에는 source의 갱신이 완료된 후에만 destination의 갱신이 시작되므로, destination은 최신 데이터를 기반으로 하게 됩니다. 또는 다음과 같이 동일한 결과를 얻을 수 있습니다:
CREATE MATERIALIZED VIEW destination REFRESH AFTER 1 HOUR DEPENDS ON source AS SELECT ... FROM source
여기서 1 HOURsource의 갱신 주기보다 짧은 임의의 기간일 수 있습니다. 종속 테이블은 의존하는 어떤 항목보다도 더 자주 갱신되지 않습니다. 이는 실제 갱신 주기를 여러 번 지정하지 않고 갱신 가능 뷰의 연쇄를 구성하는 올바른 방법입니다. 추가 예시는 다음과 같습니다:
  • REFRESH EVERY 1 DAY OFFSET 10 MINUTE (destination)는 REFRESH EVERY 1 DAY (source)에 종속됩니다.
    source의 갱신에 10분 이상 걸리면 destination은 이를 기다립니다.
  • REFRESH EVERY 1 DAY OFFSET 1 HOURREFRESH EVERY 1 DAY OFFSET 23 HOUR에 종속됩니다.
    해당 갱신이 서로 다른 날짜에 발생하더라도 위와 비슷합니다. X+1일의 destination 갱신은 X일의 source 갱신을 기다립니다(2시간 이상 걸리는 경우).
  • REFRESH EVERY 2 HOURREFRESH EVERY 1 HOUR에 종속됩니다.
    2 HOUR 갱신은 한 시간 걸러 한 번씩 1 HOUR 갱신 후에 수행됩니다. 예를 들어 자정 갱신 후, 그다음에는 오전 2시 갱신 후에 수행됩니다.
  • REFRESH EVERY 1 MINUTEREFRESH EVERY 2 HOUR에 종속됩니다.
    destinationsource가 갱신될 때마다 한 번, 즉 2시간마다 갱신됩니다. 따라서 1 MINUTE는 사실상 무시됩니다.
  • REFRESH AFTER 1 HOURREFRESH AFTER 1 HOUR에 종속됩니다.
    현재로서는 권장되지 않습니다.
DEPENDS ON은 갱신 가능 materialized view 사이에서만 작동합니다. 일반 테이블을 DEPENDS ON 목록에 포함하면 해당 뷰는 더 이상 갱신되지 않습니다(종속 항목은 ALTER로 제거할 수 있으며, 갱신 매개변수 변경을 참조하십시오).

갱신 설정

사용 가능한 갱신 설정은 다음과 같습니다.
  • refresh_retries - 갱신 쿼리가 예외로 실패한 경우 재시도할 횟수입니다. 모든 재시도가 실패하면 다음으로 예약된 갱신 시각으로 넘어갑니다. 0은 재시도하지 않음을, -1은 무한 재시도를 의미합니다. 기본값: 2.
  • refresh_retry_initial_backoff_ms - refresh_retries가 0이 아닐 때 첫 번째 재시도 전까지의 지연 시간입니다. 이후 각 재시도마다 지연 시간이 두 배로 증가하며, 최대 refresh_retry_max_backoff_ms까지 늘어납니다. 기본값: 100 ms.
  • refresh_retry_max_backoff_ms - 갱신 시도 사이의 지연 시간이 지수적으로 증가할 때의 상한입니다. 기본값: 60000 ms(1분).
  • all_replicas - APPEND를 사용하는 복제된 데이터베이스에서 모든 레플리카가 각각 독립적으로 갱신할지, 아니면 예약된 각 시각마다 하나의 레플리카만 갱신할지를 제어합니다. 이 설정은 뷰를 생성한 후에는 변경할 수 없습니다. 기본값: false.
  • prefer_dependency_replica - 뷰에 DEPENDS ON이 있으면 상위 갱신을 실행한 레플리카가 종속 갱신 실행에서 우선권을 가지며, 다른 레플리카는 prefer_dependency_replica_delay_ms만큼 시도를 지연합니다. SharedMergeTree와 함께 사용하면 복제 지연으로 인해 종속 갱신 체인에서 데이터가 누락되는 문제를 방지하는 데 유용합니다. 기본값: false.
  • prefer_dependency_replica_delay_ms - prefer_dependency_replica가 활성화된 경우, 우선순위가 없는 레플리카가 종속 갱신 실행을 시도하기 전에 대기하는 시간입니다. 기본값: 2000 ms.

갱신 매개변수 변경

기존 갱신 가능 구체화 뷰의 갱신 매개변수는 ALTER TABLE ... MODIFY REFRESH를 사용해 변경합니다:
ALTER TABLE [db.]name MODIFY REFRESH EVERY|AFTER ... [RANDOMIZE FOR ...] [DEPENDS ON ...] [SETTINGS ...]
스케줄(EVERY 또는 AFTER)은 필수입니다. 이 구문은 항상 모든 갱신 매개변수(스케줄, RANDOMIZE FOR, DEPENDS ON, 갱신 설정)를 지정된 값으로 모두 대체합니다. 생략된 항목은 기본값으로 재설정되거나(설정), 제거됩니다(의존성, 무작위화).
  • 갱신 설정만 변경하려는 경우(예: refresh_retries)에는 기존 스케줄을 다시 지정하십시오:
    ALTER TABLE rmv MODIFY REFRESH EVERY 1 HOUR SETTINGS refresh_retries = 5;
    
  • materialized view에서는 ALTER TABLE ... MODIFY SETTING refresh_retries = ...가 지원되지 않습니다. 반드시 MODIFY REFRESH를 사용해야 합니다.
  • APPEND를 추가하거나 제거하는 것은 지원되지 않습니다.
  • all_replicas 설정은 생성 후 변경할 수 없습니다.
예시:
-- 스케줄을 변경하고 기존 설정 및 의존성을 삭제합니다.
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를 사용하십시오. 특히 뷰를 생성한 후 초기 갱신이 완료될 때까지 기다릴 때 유용합니다.
재미있는 사실: 갱신 쿼리는 현재 갱신 중인 뷰를 읽을 수 있으며, 갱신 전 버전의 데이터를 보게 됩니다. 즉, Conway의 생명 게임을 구현할 수 있습니다: https://pastila.nl/?00021a4b/d6156ff819c83d490ad2dcec05676865#O0LGWTO7maUQIA4AcGUtlA==

윈도우 뷰

이 기능은 실험 단계이며, 향후 릴리스에서 하위 호환성이 깨지는 방식으로 변경될 수 있습니다. allow_experimental_window_view 설정으로 윈도우 뷰와 WATCH 쿼리 사용을 활성화하십시오. set allow_experimental_window_view = 1 명령을 입력하십시오.
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을 반드시 지정해야 합니다.

시간 윈도우 함수

시간 윈도우 함수는 레코드의 윈도우 하한과 상한을 가져오는 데 사용됩니다. 윈도우 뷰는 시간 윈도우 함수와 함께 사용해야 합니다.

시간 속성

윈도우 뷰는 **처리 시간(processing time)**과 이벤트 시간(event time) 처리를 지원합니다. **처리 시간(processing time)**은 로컬 머신의 시간을 기준으로 윈도우 뷰가 결과를 생성하도록 하며, 기본적으로 사용됩니다. 가장 직관적인 시간 개념이지만 결정성을 보장하지는 않습니다. 처리 시간 속성은 시간 윈도 함수의 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 구문을 사용해 이벤트 시간 처리를 지원합니다. 윈도우 뷰는 세 가지 워터마크 전략을 제공합니다.
  • 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;
지연 트리거로 방출된 요소는 이전 계산의 갱신된 결과로 처리해야 합니다. 윈도우가 끝날 때 트리거되는 대신, 윈도우 뷰는 지연 이벤트가 도착하는 즉시 트리거됩니다. 따라서 동일한 윈도우에 대해 여러 번 출력될 수 있습니다. 사용자는 이러한 중복 결과를 고려하거나 중복을 제거해야 합니다. ALTER TABLE ... MODIFY QUERY statement를 사용하면 윈도우 뷰에 지정된 SELECT 쿼리를 수정할 수 있습니다. 새 SELECT 쿼리의 결과 데이터 구조는 TO [db.]name 절의 유무와 관계없이 원래 SELECT 쿼리와 동일해야 합니다. 중간 상태는 재사용할 수 없으므로 현재 윈도우의 데이터는 손실된다는 점에 유의하십시오.

새 윈도우 모니터링

윈도우 뷰는 변경 사항을 모니터링하는 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: 이벤트 시간 처리에서 윈도우 뷰 실행 신호를 기다리는 시간 제한입니다.

예시

data라는 로그 테이블에서 10초마다 클릭 로그의 개수를 집계해야 한다고 가정하겠습니다. 테이블 구조는 다음과 같습니다:
CREATE TABLE data ( `id` UInt64, `timestamp` DateTime) ENGINE = Memory;
먼저, 10초 인터벌의 tumble window를 사용하는 window view를 생성합니다:
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*라는 이름을 사용합니다).

윈도우 뷰 사용

윈도우 뷰는 다음과 같은 시나리오에서 유용합니다:
  • 모니터링: 메트릭 로그를 시간별로 집계하고 계산한 뒤, 결과를 대상 테이블(target table)에 출력합니다. 대시보드는 대상 테이블을 소스 테이블로 사용할 수 있습니다.
  • 분석: 시간 윈도우에서 데이터를 자동으로 집계하고 전처리합니다. 이는 대량의 로그를 분석할 때 유용합니다. 전처리를 통해 여러 쿼리에서 반복 계산을 없애고 쿼리 지연 시간을 줄일 수 있습니다.

임시 뷰

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)을 참조하는 경우, 해당 데이터는 임시 테이블과 마찬가지로 분산 쿼리 실행 중 원격 서버로 전송될 수 있습니다.

예시

-- 세션 범위의 인메모리 테이블
CREATE TEMPORARY TABLE temp_ids (id UInt64) ENGINE = Memory;

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

-- 임시 테이블에 대한 세션 범위의 뷰 (순수 논리적)
CREATE TEMPORARY VIEW v_ids AS
SELECT id FROM temp_ids;

-- 'test'를 실제 클러스터 이름으로 변경하십시오.
-- GLOBAL JOIN은 ClickHouse가 작은 조인 측(v_ids를 통한 temp_ids)을
-- 왼쪽을 실행하는 모든 원격 서버로 *전송*하도록 강제합니다.
SELECT count()
FROM cluster('test', system.numbers) AS n
GLOBAL ANY INNER JOIN v_ids USING (id)
WHERE n.number < 100;

마지막 수정일 2026년 6월 10일