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

> CREATE VIEW에 대한 문서

# CREATE VIEW

export const CloudNotSupportedBadge = () => {
  return <div className="cloudNotSupportedBadge">
            <div className="cloudNotSupportedIcon">
            <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path strokeWidth="1.5" d="M6.33366 12.6666L12.3739 12.6667C13.6593 12.6667 14.7073 11.6187 14.7073 10.3334C14.7073 9.04804 13.6593 8.00003 12.3739 8.00003C12.3739 8.00003 12.3337 7.66659 12.0003 7.33325M10.667 5.33322C8.00033 2.33325 4.45395 4.78537 4.14195 6.68203C2.55728 6.7627 1.29395 8.06203 1.29395 9.6667C1.29395 11.3234 2.66699 12.6666 4.00033 12.6666" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
                <path strokeWidth="1.5" d="M2.66699 14L12.0003 4.66663" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
            </svg>

        </div>
            Not supported in ClickHouse Cloud
        </div>;
};

export const DeprecatedBadge = () => {
  return <div className="deprecatedBadge">
            <div className="deprecatedIcon">
            <svg width="14" height="10" viewBox="0 0 14 10" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path d="M13 0H1C0.734784 0 0.48043 0.105357 0.292893 0.292893C0.105357 0.48043 0 0.734784 0 1V2.5C0 2.76522 0.105357 3.01957 0.292893 3.20711C0.48043 3.39464 0.734784 3.5 1 3.5V9C1 9.26522 1.10536 9.51957 1.29289 9.70711C1.48043 9.89464 1.73478 10 2 10H12C12.2652 10 12.5196 9.89464 12.7071 9.70711C12.8946 9.51957 13 9.26522 13 9V3.5C13.2652 3.5 13.5196 3.39464 13.7071 3.20711C13.8946 3.01957 14 2.76522 14 2.5V1C14 0.734784 13.8946 0.48043 13.7071 0.292893C13.5196 0.105357 13.2652 0 13 0ZM12 9H2V3.5H12V9ZM13 2.5H1V1H13V2.5ZM5 5.5C5 5.36739 5.05268 5.24021 5.14645 5.14645C5.24021 5.05268 5.36739 5 5.5 5H8.5C8.63261 5 8.75979 5.05268 8.85355 5.14645C8.94732 5.24021 9 5.36739 9 5.5C9 5.63261 8.94732 5.75979 8.85355 5.85355C8.75979 5.94732 8.63261 6 8.5 6H5.5C5.36739 6 5.24021 5.94732 5.14645 5.85355C5.05268 5.75979 5 5.63261 5 5.5Z" fill="currentColor" />
            </svg>
        </div>
            Deprecated feature
        </div>;
};

export const ExperimentalBadge = () => {
  return <div className="experimentalBadge">
            <div className="experimentalIcon">
            <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path strokeWidth="1.25" d="M5.5 2H10.5" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
                <path strokeWidth="1.25" d="M9.50015 2V6.19625L13.4283 12.7425C13.4738 12.8183 13.4985 12.9049 13.4996 12.9934C13.5008 13.0818 13.4785 13.169 13.435 13.246C13.3914 13.323 13.3283 13.3871 13.2519 13.4317C13.1755 13.4764 13.0886 13.4999 13.0002 13.5H3.00015C2.91164 13.5 2.8247 13.4766 2.74822 13.432C2.67174 13.3874 2.60847 13.3233 2.56487 13.2463C2.52126 13.1693 2.49889 13.082 2.50004 12.9935C2.50119 12.905 2.52582 12.8184 2.5714 12.7425L6.50015 6.19625V2" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
                <path strokeWidth="1.25" d="M4.47656 9.56754C5.30344 9.41254 6.47656 9.47942 7.99969 10.25C10.0153 11.2707 11.4216 11.0569 12.2184 10.7282" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
            </svg>
        </div>
            Experimental feature. <u><a href="/docs/beta-and-experimental-features#experimental-features">Learn more.</a></u>
        </div>;
};

새로운 뷰를 생성합니다. 뷰는 [일반](#normal-view), [materialized](#materialized-view), [갱신 가능한 materialized](#refreshable-materialized-view), [윈도우](/ko/reference/statements/create/view#window-view) 유형으로 만들 수 있습니다.

<div id="normal-view">
  ## 일반 뷰
</div>

구문:

```sql theme={null}
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](/ko/reference/statements/select/from) 절의 서브쿼리로 사용됩니다.

예시로, 뷰를 생성했다고 가정하겠습니다:

```sql theme={null}
CREATE VIEW view AS SELECT ...
```

그리고 쿼리를 작성했습니다:

```sql theme={null}
SELECT a, b, c FROM view
```

이 쿼리는 다음 서브쿼리를 사용하는 것과 완전히 동일합니다:

```sql theme={null}
SELECT a, b, c FROM (SELECT ...)
```

<div id="parameterized-view">
  ## 매개변수화된 뷰(Parameterized View)
</div>

매개변수화된 뷰는 일반 뷰와 유사하지만, 즉시 해석되지 않는 매개변수를 포함해 생성할 수 있습니다. 이러한 뷰는 테이블 함수와 함께 사용할 수 있으며, 이 경우 함수 이름에는 뷰 이름을 지정하고 인수에는 매개변수 값을 지정합니다.

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

위 구문은 아래와 같이 매개변수를 치환하여 테이블 함수로 사용할 수 있는 테이블용 뷰를 생성합니다.

```sql theme={null}
SELECT * FROM view(column1=value1, column2=value2 ...)
```

<div id="materialized-view">
  ## Materialized View
</div>

```sql theme={null}
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']
```

```sql theme={null}
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 REPLACE`와 `IF NOT EXISTS`는 서로 배타적이므로 함께 사용할 수 없습니다. 둘을 함께 사용하면 구문 오류가 발생합니다.

<div id="create-or-replace-materialized-view">
  ### CREATE OR REPLACE MATERIALIZED VIEW
</div>

`CREATE OR REPLACE MATERIALIZED VIEW`는 기존 materialized view와 해당 내부 저장 테이블(있는 경우)을 원자적으로 대체합니다. 이 작업을 수행하려면 `Atomic` 또는 `Replicated` 데이터베이스 엔진이 필요합니다.

```sql theme={null}
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`, 그리고 모든 엔진 옵션과 호환됩니다. `POPULATE`는 `Atomic` 데이터베이스에서만 지원되며, `Replicated` 데이터베이스에서는 허용되지 않습니다(아래 `POPULATE` 참고 사항 참조).
* `CREATE VIEW` 및 `DROP VIEW` 권한이 필요합니다.

<Note>
  `CREATE OR REPLACE MATERIALIZED VIEW`는 `Atomic` 또는 `Replicated` 데이터베이스 엔진에서만 지원됩니다. `Ordinary` 데이터베이스 엔진에서는 지원되지 않습니다.
</Note>

**예시:**

```sql theme={null}
-- 내부 테이블을 사용하여 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;
```

<Tip>
  [Materialized views](/ko/concepts/features/materialized-views/cascading-materialized-views)를 사용하는 단계별 가이드는 여기에서 확인할 수 있습니다.
</Tip>

materialized view는 해당 [SELECT](/ko/reference/statements/select) 쿼리로 변환된 데이터를 저장합니다.

`TO [db].[table]` 없이 materialized view를 생성할 때는 데이터를 저장할 테이블 엔진인 `ENGINE`을 지정해야 합니다.

`TO [db].[table]`과 함께 materialized view를 생성할 때는 `POPULATE`를 함께 사용할 수 없습니다.

materialized view는 다음과 같이 동작합니다. `SELECT`에 지정된 테이블에 데이터를 삽입하면, 삽입된 데이터의 일부가 이 `SELECT` 쿼리로 변환되고 그 결과가 뷰에 삽입됩니다.

<Note>
  ClickHouse의 materialized view는 대상 테이블에 삽입할 때 컬럼 순서가 아니라 **column names**를 사용합니다. `SELECT` 쿼리 결과에 일부 컬럼 이름이 없으면, 해당 컬럼이 [널 허용](/ko/reference/data-types/nullable)이 아니더라도 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_errors`가 `true`로 설정되어 있다는 점도 유의하십시오.
</Note>

`POPULATE`를 지정하면, 기존 테이블 데이터가 `CREATE TABLE ... AS SELECT ...`를 수행한 것처럼 생성 시 뷰에 삽입됩니다. 그렇지 않으면 쿼리에는 뷰를 생성한 후 테이블에 삽입된 데이터만 포함됩니다. 뷰를 생성하는 동안 테이블에 삽입된 데이터는 뷰에 삽입되지 않으므로, `POPULATE` 사용은 **권장하지 않습니다**.

<Note>
  `POPULATE`는 `CREATE TABLE ... AS SELECT ...`처럼 동작하므로 다음과 같은 제한이 있습니다.

  * 복제된 데이터베이스에서는 지원되지 않습니다
  * ClickHouse Cloud에서는 지원되지 않습니다

  대신 별도의 `INSERT ... SELECT`를 사용할 수 있습니다.
</Note>

`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](/ko/reference/settings/session-settings#optimize_on_insert) 설정의 영향을 받는다는 점에 유의하십시오. 데이터는 뷰에 삽입되기 전에 머지됩니다.

뷰는 일반 테이블과 동일하게 표시됩니다. 예를 들어 `SHOW TABLES` 쿼리 결과에 나열됩니다.

뷰를 삭제하려면 [DROP VIEW](/ko/reference/statements/drop#drop-view)를 사용하십시오. `DROP TABLE`도 VIEW에 사용할 수 있습니다.

<div id="sql_security">
  ## SQL 보안
</div>

`DEFINER` 및 `SQL SECURITY`를 사용하면 뷰의 기반 쿼리를 실행할 때 어떤 ClickHouse 사용자를 사용할지 지정할 수 있습니다.
`SQL SECURITY`에는 `DEFINER`, `INVOKER`, `NONE`의 세 가지 유효한 값이 있습니다. `DEFINER` 절에는 기존 사용자 또는 `CURRENT_USER`를 지정할 수 있습니다.

다음 표는 뷰에서 `SELECT`할 때 어떤 사용자에게 어떤 권한이 필요한지 설명합니다.
SQL 보안 옵션과 관계없이, 어떤 경우에도 뷰를 읽으려면 여전히 `GRANT SELECT ON <view>` 권한이 필요합니다.

| SQL security option | View                                         | Materialized View                                                       |
| ------------------- | -------------------------------------------- | ----------------------------------------------------------------------- |
| `DEFINER alice`     | `alice`는 뷰의 소스 테이블에 대한 `SELECT` 권한이 있어야 합니다. | `alice`는 뷰의 소스 테이블에 대한 `SELECT` 권한과 뷰의 대상 테이블에 대한 `INSERT` 권한이 있어야 합니다. |
| `INVOKER`           | 사용자는 뷰의 소스 테이블에 대한 `SELECT` 권한이 있어야 합니다.     | materialized view에는 `SQL SECURITY INVOKER`를 지정할 수 없습니다.                 |
| `NONE`              | -                                            | -                                                                       |

<Note>
  `SQL SECURITY NONE`은 더 이상 권장되지 않는 옵션입니다. `SQL SECURITY NONE`으로 뷰를 생성할 수 있는 권한이 있는 사용자는 임의의 쿼리를 실행할 수 있습니다.
  따라서 이 옵션으로 뷰를 생성하려면 `GRANT ALLOW SQL SECURITY NONE TO <user>` 권한이 필요합니다.
</Note>

`DEFINER`/`SQL SECURITY`를 지정하지 않으면 기본값이 사용됩니다:

* `SQL SECURITY`: 일반 뷰에는 `INVOKER`, materialized view에는 `DEFINER`([설정으로 구성 가능](/ko/reference/settings/session-settings#default_normal_view_sql_security))
* `DEFINER`: `CURRENT_USER`([설정으로 구성 가능](/ko/reference/settings/session-settings#default_view_definer))

`DEFINER`/`SQL SECURITY`를 지정하지 않고 뷰를 ATTACH하면, materialized view의 기본값은 `SQL SECURITY NONE`이고 일반 뷰의 기본값은 `SQL SECURITY INVOKER`입니다.

기존 뷰의 SQL 보안을 변경하려면 다음을 사용합니다:

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

<div id="examples">
  ### 예시
</div>

```sql theme={null}
CREATE VIEW test_view
DEFINER = alice SQL SECURITY DEFINER
AS SELECT ...
```

```sql theme={null}
CREATE VIEW test_view
SQL SECURITY INVOKER
AS SELECT ...
```

<div id="live-view">
  ## 라이브 view
</div>

이 기능은 더 이상 사용이 권장되지 않으며, 향후 제거될 예정입니다.

편의를 위해 이전 문서는 [여기](https://pastila.nl/?00f32652/fdf07272a7b54bda7e13b919264e449f.md)에서 확인하실 수 있습니다.

<div id="refreshable-materialized-view">
  ## 갱신 가능 materialized view
</div>

```sql theme={null}
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`은 단순한 인터벌의 연속입니다:

```sql theme={null}
number SECOND|MINUTE|HOUR|DAY|WEEK|MONTH|YEAR
```

해당 쿼리를 주기적으로 실행하고 결과를 테이블에 저장합니다.

* `APPEND`를 지정하면 각 갱신 시 기존 행을 삭제하지 않고 테이블에 행을 삽입합니다. 이 삽입은 일반적인 `INSERT INTO ... SELECT` 쿼리와 마찬가지로 원자적이지 않습니다.
* 그렇지 않으면 각 갱신 시 테이블의 이전 내용이 원자적으로 대체됩니다.

일반적인 비갱신형 materialized view와의 차이점:

* 삽입 트리거가 없습니다. `SELECT`에 지정된 테이블에 새 데이터가 삽입되어도 갱신 가능 materialized view로 자동 반영되지 *않습니다*. 대신 데이터 삽입은 주기적 또는 수동 갱신 실행 중에만 이루어집니다.
* `SELECT` 쿼리에는 제한이 없습니다. 테이블 함수(예: `url()`), 뷰, UNION, JOIN을 모두 사용할 수 있습니다.

<Note>
  쿼리의 `REFRESH ... SETTINGS` 부분에 있는 설정은 갱신 설정(예: `refresh_retries`)이며, 일반 설정(예: `max_threads`)과는 구분됩니다. 일반 설정은 쿼리 끝에 `SETTINGS`를 사용해 지정할 수 있습니다.
</Note>

<div id="refresh-schedule">
  ### 갱신 일정
</div>

예시 갱신 일정:

```sql theme={null}
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`는 각 갱신 시각을 무작위로 조정합니다. 예:

```sql theme={null}
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`를 지정하면 첫 번째 갱신은 일정에 따라 수행됩니다.

<div id="in-replicated-db">
  ### 복제된 DB에서
</div>

갱신 가능 구체화 뷰가 [복제된 데이터베이스](/ko/reference/engines/database-engines/replicated)에 있는 경우, 레플리카는 서로 coordination하여 예약된 각 시점마다 하나의 레플리카만 갱신을 수행합니다. 갱신으로 생성된 데이터를 모든 레플리카가 확인할 수 있도록 [ReplicatedMergeTree](/ko/reference/engines/table-engines/mergetree-family/replication) 테이블 엔진이 필요합니다.

`APPEND` 모드에서는 `SETTINGS all_replicas = 1`을 사용해 coordination을 비활성화할 수 있습니다. 이렇게 하면 각 레플리카가 서로 독립적으로 갱신을 수행합니다. 이 경우 ReplicatedMergeTree는 필요하지 않습니다.

`APPEND`가 아닌 모드에서는 coordination된 갱신만 지원됩니다. coordination되지 않은 방식을 사용하려면 `Atomic` 데이터베이스와 `CREATE ... ON CLUSTER` 쿼리를 사용해 모든 레플리카에 갱신 가능 구체화 뷰를 생성하십시오.

coordination은 Keeper를 통해 수행됩니다. znode 경로는 [default\_replica\_path](/ko/reference/settings/server-settings/settings#default_replica_path) 서버 설정으로 결정됩니다.

<div id="refresh-dependencies">
  ### 갱신 종속성
</div>

`DEPENDS ON`은 서로 다른 테이블의 갱신을 동기화합니다. 예를 들어, 2개의 갱신 가능 materialized view로 이루어진 체인이 있다고 가정하겠습니다.

```sql theme={null}
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`에 있는 전날 데이터가 표시됩니다. 종속성을 추가하면:

```sql theme={null}
CREATE MATERIALIZED VIEW destination REFRESH EVERY 1 DAY DEPENDS ON source AS SELECT ... FROM source
```

그러면 해당 날짜에는 `source`의 갱신이 완료된 후에만 `destination`의 갱신이 시작되므로, `destination`은 최신 데이터를 기반으로 하게 됩니다.

또는 다음과 같이 동일한 결과를 얻을 수 있습니다:

```sql theme={null}
CREATE MATERIALIZED VIEW destination REFRESH AFTER 1 HOUR DEPENDS ON source AS SELECT ... FROM source
```

여기서 `1 HOUR`는 `source`의 갱신 주기보다 짧은 임의의 기간일 수 있습니다. 종속 테이블은 의존하는 어떤 항목보다도 더 자주 갱신되지 않습니다. 이는 실제 갱신 주기를 여러 번 지정하지 않고 갱신 가능 뷰의 연쇄를 구성하는 올바른 방법입니다.

추가 예시는 다음과 같습니다:

* `REFRESH EVERY 1 DAY OFFSET 10 MINUTE` (`destination`)는 `REFRESH EVERY 1 DAY` (`source`)에 종속됩니다.<br />
  `source`의 갱신에 10분 이상 걸리면 `destination`은 이를 기다립니다.
* `REFRESH EVERY 1 DAY OFFSET 1 HOUR`는 `REFRESH EVERY 1 DAY OFFSET 23 HOUR`에 종속됩니다.<br />
  해당 갱신이 서로 다른 날짜에 발생하더라도 위와 비슷합니다.
  `X+1`일의 `destination` 갱신은 `X`일의 `source` 갱신을 기다립니다(2시간 이상 걸리는 경우).
* `REFRESH EVERY 2 HOUR`는 `REFRESH EVERY 1 HOUR`에 종속됩니다.<br />
  `2 HOUR` 갱신은 한 시간 걸러 한 번씩 `1 HOUR` 갱신 후에 수행됩니다. 예를 들어 자정
  갱신 후, 그다음에는 오전 2시 갱신 후에 수행됩니다.
* `REFRESH EVERY 1 MINUTE`는 `REFRESH EVERY 2 HOUR`에 종속됩니다.<br />
  `destination`은 `source`가 갱신될 때마다 한 번, 즉 2시간마다 갱신됩니다. 따라서 `1 MINUTE`는 사실상 무시됩니다.
* `REFRESH AFTER 1 HOUR`는 `REFRESH AFTER 1 HOUR`에 종속됩니다.<br />
  현재로서는 권장되지 않습니다.

<Note>
  `DEPENDS ON`은 갱신 가능 materialized view 사이에서만 작동합니다. 일반 테이블을 `DEPENDS ON` 목록에 포함하면 해당 뷰는 더 이상 갱신되지 않습니다(종속 항목은 `ALTER`로 제거할 수 있으며, [갱신 매개변수 변경](#changing-refresh-parameters)을 참조하십시오).
</Note>

<div id="refresh-settings">
  ### 갱신 설정
</div>

사용 가능한 갱신 설정은 다음과 같습니다.

* `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`를 사용하는 [복제된 데이터베이스](/ko/reference/engines/database-engines/replicated)에서 모든 레플리카가 각각 독립적으로 갱신할지, 아니면 예약된 각 시각마다 하나의 레플리카만 갱신할지를 제어합니다. 이 설정은 뷰를 생성한 후에는 변경할 수 없습니다. 기본값: `false`.
* `prefer_dependency_replica` - 뷰에 `DEPENDS ON`이 있으면 상위 갱신을 실행한 레플리카가 종속 갱신 실행에서 우선권을 가지며, 다른 레플리카는 `prefer_dependency_replica_delay_ms`만큼 시도를 지연합니다. `SharedMergeTree`와 함께 사용하면 복제 지연으로 인해 종속 갱신 체인에서 데이터가 누락되는 문제를 방지하는 데 유용합니다. 기본값: `false`.
* `prefer_dependency_replica_delay_ms` - `prefer_dependency_replica`가 활성화된 경우, 우선순위가 없는 레플리카가 종속 갱신 실행을 시도하기 전에 대기하는 시간입니다. 기본값: 2000 ms.

<div id="changing-refresh-parameters">
  ### 갱신 매개변수 변경
</div>

기존 갱신 가능 구체화 뷰의 갱신 매개변수는 [`ALTER TABLE ... MODIFY REFRESH`](/ko/reference/statements/alter/view#alter-table--modify-refresh-statement)를 사용해 변경합니다:

```sql theme={null}
ALTER TABLE [db.]name MODIFY REFRESH EVERY|AFTER ... [RANDOMIZE FOR ...] [DEPENDS ON ...] [SETTINGS ...]
```

스케줄(`EVERY` 또는 `AFTER`)은 필수입니다. 이 구문은 항상 모든 갱신 매개변수(스케줄, `RANDOMIZE FOR`, `DEPENDS ON`, 갱신 설정)를 지정된 값으로 *모두* 대체합니다. 생략된 항목은 기본값으로 재설정되거나(설정), 제거됩니다(의존성, 무작위화).

<Note>
  * 갱신 설정만 변경하려는 경우(예: `refresh_retries`)에는 기존 스케줄을 다시 지정하십시오:

    ```sql theme={null}
    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` 설정은 생성 후 변경할 수 없습니다.
</Note>

예시:

```sql theme={null}
-- 스케줄을 변경하고 기존 설정 및 의존성을 삭제합니다.
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;
```

<div id="other-operations">
  ### 기타 작업
</div>

모든 갱신 가능 materialized view의 상태는 [`system.view_refreshes`](/ko/reference/system-tables/view_refreshes) 테이블에서 확인할 수 있습니다. 특히 갱신 진행 상황(실행 중인 경우), 마지막 및 다음 갱신 시각, 갱신이 실패한 경우의 예외 메시지가 포함됩니다.

갱신을 수동으로 중지, 시작, 실행 또는 취소하려면 [`SYSTEM STOP|START|REFRESH|WAIT|CANCEL VIEW`](/ko/reference/statements/system#managing-refreshable-materialized-views)를 사용하십시오.

갱신이 완료될 때까지 기다리려면 [`SYSTEM WAIT VIEW`](/ko/reference/statements/system#wait-view)를 사용하십시오. 특히 뷰를 생성한 후 초기 갱신이 완료될 때까지 기다릴 때 유용합니다.

<Note>
  재미있는 사실: 갱신 쿼리는 현재 갱신 중인 뷰를 읽을 수 있으며, 갱신 전 버전의 데이터를 보게 됩니다. 즉, Conway의 생명 게임을 구현할 수 있습니다: [https://pastila.nl/?00021a4b/d6156ff819c83d490ad2dcec05676865#O0LGWTO7maUQIA4AcGUtlA==](https://pastila.nl/?00021a4b/d6156ff819c83d490ad2dcec05676865#O0LGWTO7maUQIA4AcGUtlA==)
</Note>

<div id="window-view">
  ## 윈도우 뷰
</div>

<Info>
  이 기능은 실험 단계이며, 향후 릴리스에서 하위 호환성이 깨지는 방식으로 변경될 수 있습니다. [allow\_experimental\_window\_view](/ko/reference/settings/session-settings#allow_experimental_window_view) 설정으로 윈도우 뷰와 `WATCH` 쿼리 사용을 활성화하십시오. `set allow_experimental_window_view = 1` 명령을 입력하십시오.
</Info>

```sql theme={null}
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`을 반드시 지정해야 합니다.

<div id="time-window-functions">
  ### 시간 윈도우 함수
</div>

[시간 윈도우 함수](/ko/reference/functions/regular-functions/time-window-functions)는 레코드의 윈도우 하한과 상한을 가져오는 데 사용됩니다. 윈도우 뷰는 시간 윈도우 함수와 함께 사용해야 합니다.

<div id="time-attributes">
  ### 시간 속성
</div>

윈도우 뷰는 \*\*처리 시간(processing time)\*\*과 **이벤트 시간(event time)** 처리를 지원합니다.

\*\*처리 시간(processing time)\*\*은 로컬 머신의 시간을 기준으로 윈도우 뷰가 결과를 생성하도록 하며, 기본적으로 사용됩니다. 가장 직관적인 시간 개념이지만 결정성을 보장하지는 않습니다. 처리 시간 속성은 시간 윈도 함수의 `time_attr`를 테이블 컬럼으로 설정하거나 `now()` 함수를 사용해 정의할 수 있습니다. 다음 쿼리는 처리 시간을 사용하는 윈도우 뷰를 생성합니다.

```sql theme={null}
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`를 사용하여 윈도우 뷰를 생성하는 예시입니다.

```sql theme={null}
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`을 설정해 지연 이벤트 처리를 지원합니다. 지연 처리의 예시는 다음과 같습니다.

```sql theme={null}
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` 쿼리와 동일해야 합니다. 중간 상태는 재사용할 수 없으므로 현재 윈도우의 데이터는 손실된다는 점에 유의하십시오.

<div id="monitoring-new-windows">
  ### 새 윈도우 모니터링
</div>

윈도우 뷰는 변경 사항을 모니터링하는 [WATCH](/ko/reference/statements/watch) 쿼리를 지원하며, `TO` 구문을 사용해 결과를 테이블로 출력할 수도 있습니다.

```sql theme={null}
WATCH [db.]window_view
[EVENTS]
[LIMIT n]
[FORMAT format]
```

쿼리를 종료하기 전에 받을 업데이트 횟수를 설정하려면 `LIMIT`를 지정할 수 있습니다. `EVENTS` 절을 사용하면 `WATCH` 쿼리의 축약형을 사용할 수 있으며, 쿼리 결과 대신 최신 쿼리 워터마크만 받습니다.

<div id="settings-1">
  ### 설정
</div>

* `window_view_clean_interval`: 오래된 데이터를 정리하기 위한 윈도우 뷰 정리 간격(초)입니다. 시스템 시간 또는 `WATERMARK` 구성에 따라 아직 완전히 트리거되지 않은 윈도우는 유지되며, 그 외 데이터는 삭제됩니다.
* `window_view_heartbeat_interval`: watch 쿼리가 살아 있음을 나타내는 하트비트 간격(초)입니다.
* `wait_for_window_view_fire_signal_timeout`: 이벤트 시간 처리에서 윈도우 뷰 실행 신호를 기다리는 시간 제한입니다.

<div id="examples">
  ### 예시
</div>

`data`라는 로그 테이블에서 10초마다 클릭 로그의 개수를 집계해야 한다고 가정하겠습니다. 테이블 구조는 다음과 같습니다:

```sql theme={null}
CREATE TABLE data ( `id` UInt64, `timestamp` DateTime) ENGINE = Memory;
```

먼저, 10초 인터벌의 tumble window를 사용하는 window view를 생성합니다:

```sql theme={null}
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` 쿼리를 사용해 결과를 확인합니다.

```sql theme={null}
WATCH wv
```

로그가 `data` 테이블에 삽입되면,

```sql theme={null}
INSERT INTO data VALUES(1,now())
```

`WATCH` 쿼리는 다음과 같이 결과가 출력되어야 합니다:

```text theme={null}
┌─count(id)─┬────────window_start─┐
│         1 │ 2020-01-14 16:56:40 │
└───────────┴─────────────────────┘
```

또는 `TO` 구문을 사용해 출력을 다른 테이블로 보낼 수도 있습니다.

```sql theme={null}
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*`라는 이름을 사용합니다).

<div id="window-view-usage">
  ### 윈도우 뷰 사용
</div>

윈도우 뷰는 다음과 같은 시나리오에서 유용합니다:

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

<div id="related-content">
  ## 관련 콘텐츠
</div>

* 블로그: [ClickHouse에서 시계열 데이터 다루기](https://clickhouse.com/blog/working-with-time-series-data-and-functions-ClickHouse)
* 블로그: [ClickHouse로 관측성 솔루션 구축하기 - 2부 - 트레이스](https://clickhouse.com/blog/storing-traces-and-spans-open-telemetry-in-clickhouse)

<div id="temporary-views">
  ## 임시 뷰
</div>

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;`를 사용하십시오.

<div id="temporary-views-syntax">
  ### 구문
</div>

```sql theme={null}
CREATE TEMPORARY VIEW [IF NOT EXISTS] view_name AS <select_query>
```

`OR REPLACE`는 임시 뷰에서는 지원되지 않습니다(임시 테이블과 일관성을 맞추기 위함입니다). 임시 뷰를 “대체”해야 하는 경우, 해당 뷰를 삭제한 후 다시 생성하십시오.

<div id="examples">
  ### 예시
</div>

먼저 임시 원본 테이블을 만들고, 그 위에 임시 뷰를 생성합니다:

```sql theme={null}
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을 확인합니다:

```sql theme={null}
SHOW CREATE TEMPORARY VIEW tview;
```

삭제:

```sql theme={null}
DROP TEMPORARY VIEW IF EXISTS tview;  -- 임시 뷰는 TEMPORARY TABLE 구문으로 삭제됩니다
```

<div id="temporary-views-limitations">
  ### 지원되지 않는 항목 / 제한 사항
</div>

* `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 전용 절 → 임시 뷰에는 **적용되지 않음**.

<div id="temporary-views-distributed-notes">
  ### 분산 쿼리 관련 참고 사항
</div>

임시 **뷰**는 단지 정의일 뿐이므로, 별도로 전달할 데이터는 없습니다. 임시 뷰가 임시 **테이블**(예: `Memory`)을 참조하는 경우, 해당 데이터는 임시 테이블과 마찬가지로 분산 쿼리 실행 중 원격 서버로 전송될 수 있습니다.

<div id="temporary-views-distributed-example">
  #### 예시
</div>

```sql theme={null}
-- 세션 범위의 인메모리 테이블
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;

```
