Создает новое представление. Представления бывают обычными, materialized, refreshable 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 ...)Параметризованное представление
Параметризованные представления похожи на обычные представления, но могут создаваться с параметрами, значения которых определяются не сразу. Эти представления можно использовать с табличными функциями: имя представления указывается как имя функции, а значения параметров — как её аргументы.
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 REPLACE и IF 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и любыми параметрами движка.POPULATEподдерживается только в базах данныхAtomic— в базах данных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 views хранят данные, преобразованные соответствующим запросом SELECT.
При создании materialized view без TO [db].[table] необходимо указать ENGINE — движок таблицы для хранения данных.
При создании materialized view с TO [db].[table] также можно использовать POPULATE для дозагрузки целевой таблицы существующими исходными данными (целевая таблица может уже содержать данные; в этом случае дозагружаемые строки добавляются). POPULATE нельзя сочетать с REFRESH: refreshable materialized view заполняется при первом обновлении, поэтому POPULATE загрузил бы исходные данные дважды (вместо этого используйте EMPTY, чтобы пропустить первое обновление).
Materialized view работает следующим образом: при вставке данных в таблицу, указанную в SELECT, часть вставленных данных преобразуется этим запросом SELECT, а результат вставляется в представление.
Если указать POPULATE, существующие данные исходной таблицы будут вставлены в представление при его создании. В противном случае представление содержит только данные, вставленные в исходную таблицу после создания представления.
Для обычного CREATE MATERIALIZED VIEW операция POPULATE по умолчанию атомарна (настройка materialized_views_populate_atomically = 1): представление подписывается на новые вставки в исходную таблицу, а снимок существующих данных создаётся одновременно с этим под кратковременной эксклюзивной блокировкой исходной таблицы. Поэтому каждая строка, вставленная одновременно с заполнением, доставляется в представление ровно один раз — без пропусков и дубликатов. Затем заполнение (которое может выполняться долго) читает закреплённый снимок, не удерживая блокировку.
Это атомарность локального пути вставки: эксклюзивная блокировка сериализуется только со вставками, которые получают блокировку хранилища этой исходной таблицы на том же сервере, поэтому гарантия exactly-once распространяется на вставки, поступающие через этот сервер. Это не гарантия для всего кластера — строки, вставленные в другую реплику источника ReplicatedMergeTree или через распределённый путь записи (например, в таблицу Distributed или через ON CLUSTER) одновременно с заполнением, находятся за пределами этой отсечки и всё ещё могут быть пропущены или продублированы.
Если заполнение завершается ошибкой — например, эксклюзивная блокировка занятой исходной таблицы не может быть получена за время lock_acquire_timeout или SELECT представления генерирует исключение при выполнении, — только что созданное представление удаляется, а запрос CREATE завершается ошибкой, не оставляя ничего из созданного, поэтому его можно просто повторить. Для формы TO [db].[table] этот откат удаляет только представление, но никогда не удаляет уже существующую целевую таблицу — однако строки, уже вставленные неудавшимся заполнением в целевую таблицу, остаются в ней, как и после неудавшегося INSERT ... SELECT в эту таблицу, поэтому повторный CREATE вставляет их снова. Если обратное заполнение должно быть точным, повторите операцию с очищенной или новой целевой таблицей либо используйте движок с дедупликацией, например ReplacingMergeTree.
Запрос SELECT может содержать DISTINCT, GROUP BY, ORDER BY, LIMIT. Обратите внимание, что соответствующие преобразования выполняются независимо для каждого блока вставленных данных. Например, если задан GROUP BY, данные агрегируются во время вставки, но только в пределах одного пакета вставленных данных. Далее данные не агрегируются. Исключение — использование ENGINE, который сам выполняет агрегацию данных, например SummingMergeTree.
Если materialized view использует конструкцию TO [db.]name, можно выполнить DETACH представления, запустить ALTER для целевой таблицы, а затем ATTACH ранее отсоединённого (DETACH) представления.
Представления выглядят так же, как обычные таблицы. Например, они отображаются в результате запроса SHOW TABLES.
Чтобы удалить представление, используйте DROP VIEW. Хотя DROP TABLE тоже работает для VIEW.
Безопасность SQL
DEFINER и SQL SECURITY позволяют указать, от имени какого пользователя ClickHouse выполнять запрос, лежащий в основе представления.
SQL SECURITY имеет три допустимых значения: DEFINER, INVOKER или NONE. В секции DEFINER можно указать любого существующего пользователя или CURRENT_USER.
В следующей таблице показано, какие права требуются и какому пользователю, чтобы выполнять SELECT из представления.
Обратите внимание: независимо от выбранного режима безопасности SQL, в любом случае для чтения из представления по-прежнему требуется GRANT SELECT ON <view>.
| Параметр безопасности SQL | Представление | Materialized View |
|---|---|---|
DEFINER alice |
У alice должен быть grant SELECT на исходную таблицу представления. |
У alice должен быть grant SELECT на исходную таблицу представления и grant INSERT на целевую таблицу представления. |
INVOKER |
У пользователя должен быть grant SELECT на исходную таблицу представления. |
SQL SECURITY INVOKER нельзя указывать для materialized view. |
NONE |
- | - |
Если DEFINER/SQL SECURITY не указаны, результат зависит от настройки сервера ignore_empty_sql_security_in_create_view_query.
При значении по умолчанию true запрос сохраняется в исходном виде, а представление получает пустой тип безопасности SQL. Обычное представление при этом выполняется с разрешениями вызывающего пользователя, а для materialized view с явно указанной целевой таблицей проверки доступа к этой целевой таблице пропускаются: вставка в исходную таблицу не требует привилегии INSERT на целевую таблицу, а чтение из представления не требует привилегии SELECT на неё.
При значении false в определение представления при создании записываются следующие значения по умолчанию:
SQL SECURITY:INVOKERдля обычных представлений (настраивается черезdefault_normal_view_sql_security) иDEFINERдля materialized view (настраивается черезdefault_materialized_view_sql_security)DEFINER:CURRENT_USER(настраивается черезdefault_view_definer)
Refreshable materialized view всегда получают эти значения по умолчанию независимо от настройки.
При присоединении или перезагрузке представления при запуске сервера оно сохраняет тип безопасности SQL из своего сохранённого определения, поэтому представление, сохранённое без DEFINER/SQL SECURITY, сохраняет пустой тип безопасности SQL.
Чтобы изменить безопасность 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 ...Live View
Эта возможность объявлена устаревшей и будет удалена в будущем.
Для вашего удобства старая документация находится здесь
Refreshable 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 должно быть указано как минимум одно из EVERY, AFTER или DEPENDS ON. Просто REFRESH (без них) не допускается. REFRESH DEPENDS ON ... без EVERY/AFTER — это сокращение для REFRESH AFTER 0 SECOND DEPENDS ON ...; см. ниже раздел Refresh Dependencies.
Периодически выполняет соответствующий запрос и сохраняет его результат в таблице.
- Если указано
APPEND, при каждом обновлении в таблицу добавляются новые строки без удаления существующих. Вставка не является атомарной, как и в обычном запросеINSERT INTO ... SELECT. - В противном случае при каждом обновлении предыдущее содержимое таблицы атомарно заменяется.
Отличия от обычных non-refreshable materialized views:
- Нет insert trigger. Когда новые данные вставляются в таблицу, указанную в
SELECT, они не передаются автоматически в refreshable 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 5RANDOMIZE FOR случайным образом смещает время каждого обновления, например:
REFRESH EVERY 1 DAY OFFSET 2 HOUR RANDOMIZE FOR 1 HOUR -- every day at random time between 01:30 and 02:30Для одного представления одновременно может выполняться не более одного обновления. Например, если обновление представления с REFRESH EVERY 1 MINUTE занимает 2 минуты, оно будет обновляться раз в 2 минуты. Если затем оно начнёт выполняться быстрее и обновляться за 10 секунд, оно снова вернётся к обновлению раз в минуту. (В частности, оно не будет обновляться каждые 10 секунд, чтобы наверстать пропущенные обновления — никакой очереди таких обновлений не существует.)
Обычно первое обновление запускается сразу после создания materialized view: время с момента последнего обновления равно бесконечности, поэтому по любому расписанию обновление должно начаться прямо сейчас. Если указано EMPTY, это начальное обновление пропускается, а первое обновление произойдёт в следующий момент по расписанию; например, для EVERY 1 HOUR первое обновление произойдёт в конце текущего часа.
В базе данных Replicated
Если refreshable materialized view находится в базе данных Replicated, реплики координируют работу между собой так, что в каждый запланированный момент обновление выполняет только одна реплика. Для этого требуется движок таблицы ReplicatedMergeTree, чтобы все реплики видели данные, полученные в результате обновления.
В режиме APPEND координацию можно отключить с помощью SETTINGS all_replicas = 1. В этом случае реплики выполняют обновления независимо друг от друга. Тогда ReplicatedMergeTree не требуется.
В режиме без APPEND поддерживается только координируемое обновление. Для нескоординированного обновления используйте базу данных Atomic и запрос CREATE ... ON CLUSTER, чтобы создать refreshable materialized view на всех репликах.
Координация выполняется через Keeper. Путь znode определяется настройкой сервера 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]И зависимость, и зависящий от неё объект могут независимо пропускать временные интервалы, если обновление занимает больше времени, чем период обновления. Нет гарантии, что зависимый объект будет обновляться ровно один раз на каждое обновление зависимости.
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. REFRESH AFTER T добавляет задержку: зависимое представление начнет обновляться через T после завершения обновления зависимости.
Циклические зависимости допустимы и полезны. Рассмотрим такой граф refreshable materialized views:
- X берет батч строк из некоторого потока и помещает их в таблицу.
- Затем Y и Z читают из этой таблицы, выполняют разные агрегации и добавляют результаты в другие таблицы.
- После полной обработки батча X берет следующий батч, и цикл повторяется.
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не равно нулю. При каждой следующей повторной попытке задержка удваивается, вплоть доrefresh_retry_max_backoff_ms. Значение по умолчанию: 100 мс.refresh_retry_max_backoff_ms- Ограничение на экспоненциальный рост задержки между попытками обновления. Значение по умолчанию: 60000 мс (1 минута).all_replicas- В Replicated database сAPPENDопределяет, будут ли все реплики обновляться независимо или в каждый запланированный момент обновление будет выполнять только одна реплика. Не может быть изменено после создания представления. Значение по умолчанию:false.
Изменение параметров обновления
Параметры обновления существующей refreshable materialized view изменяются командой ALTER TABLE ... MODIFY REFRESH:
ALTER TABLE [db.]name MODIFY REFRESH EVERY|AFTER ... [RANDOMIZE FOR ...] [DEPENDS ON ...] [SETTINGS ...]Расписание (EVERY или AFTER) обязательно: этот оператор всегда заменяет все параметры обновления — расписание, RANDOMIZE FOR, DEPENDS 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;Другие операции
Статус всех refreshable materialized view доступен в таблице system.view_refreshes. В частности, в ней содержатся прогресс обновления (если оно выполняется), время последнего и следующего обновления, а также сообщение об исключении, если обновление завершилось ошибкой.
Чтобы вручную остановить, запустить, инициировать или отменить обновления, используйте SYSTEM STOP|START|REFRESH|WAIT|CANCEL VIEW.
Чтобы дождаться завершения обновления, используйте SYSTEM WAIT VIEW. Это особенно полезно, если нужно дождаться первого обновления после создания представления.
Оконное представление
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.
Оконное представление поддерживает три стратегии водяной метки:
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;Обратите внимание, что элементы, выдаваемые при позднем срабатывании, следует рассматривать как обновлённые результаты предыдущего вычисления. Вместо срабатывания в конце окна оконное представление срабатывает сразу при поступлении позднего события. Таким образом, для одного и того же окна будет сформировано несколько результатов. Пользователям нужно учитывать эти дублирующиеся результаты или выполнять их дедупликацию.
Вы можете изменить SELECT запрос, указанный в оконном представлении, с помощью оператора ALTER TABLE ... MODIFY QUERY. Структура данных, получающаяся в результате выполнения нового SELECT запроса, должна быть такой же, как у исходного SELECT запроса, как с предложением TO [db.]name, так и без него. Обратите внимание, что данные в текущем окне будут потеряны, поскольку промежуточное состояние нельзя использовать повторно.
Мониторинг новых окон
Оконное представление поддерживает запрос WATCH для мониторинга изменений, либо можно использовать синтаксис TO для вывода результатов в таблицу.
WATCH [db.]window_view
[EVENTS]
[LIMIT n]
[FORMAT format]Можно указать LIMIT, чтобы задать количество обновлений, которые нужно получить до завершения запроса. Предложение EVENTS позволяет использовать сокращённую форму запроса WATCH: вместо результата запроса вы получите только последнюю водяную метку запроса.
Настройки
window_view_clean_interval: Интервал очистки оконного представления в секундах для удаления устаревших данных. Система сохраняет окна, которые ещё не были полностью активированы в соответствии с системным временем или конфигурациейWATERMARK, а остальные данные удаляются.window_view_heartbeat_interval: Интервал heartbeat-сигнала в секундах, показывающий, что watch-запрос активен.wait_for_window_view_fire_signal_timeout: Тайм-аут ожидания сигнала срабатывания оконного представления при обработке по времени события.
Пример
Предположим, нам нужно подсчитать количество записей о кликах за каждые 10 секунд в таблице журналов data, и структура этой таблицы такова:
CREATE TABLE data ( `id` UInt64, `timestamp` DateTime) ENGINE = Memory;Сначала создадим оконное представление с окном tumble с 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
- Блог: Создание решения для обсервабилити на ClickHouse — часть 2 — трейсы
Временные представления
ClickHouse поддерживает временные представления со следующими характеристиками (где применимо — аналогично временным таблицам):
-
Срок жизни сеанса Временное представление существует только в рамках текущего сеанса. По завершении сеанса оно удаляется автоматически.
-
Без базы данных Для временного представления нельзя указывать имя базы данных. Оно существует вне баз данных (в пространстве имен сеанса).
-
Не реплицируется / без ON CLUSTER Временные объекты локальны для сеанса и не могут создаваться с
ON CLUSTER. -
Разрешение имен Если временный объект (таблица или представление) имеет то же имя, что и постоянный объект, и запрос ссылается на это имя без указания базы данных, используется временный объект.
-
Логический объект (без хранения) Временное представление хранит только текст своего
SELECT(внутри используется хранилищеView). Оно не сохраняет данные и не поддерживаетINSERT. -
Предложение ENGINE Указывать
ENGINEне требуется; если указатьENGINE = View, оно будет проигнорировано / обработано как то же логическое представление. -
Безопасность / привилегии Для создания временного представления требуется привилегия
CREATE TEMPORARY VIEW, которая неявно предоставляется черезCREATE VIEW. -
SHOW CREATE Используйте
SHOW CREATE TEMPORARY VIEW view_name;, чтобы вывести DDL временного представления.
Синтаксис
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;