Материализованные представления

Материализованное представление (materialized view) вычисляет промежуточные агрегатные состояния для данных другой таблицы (source table) согласно запросу SELECT, который указывается в описании представления, и сохраняет результаты в свою внутреннюю или отдельную таблицу (target table). Обновление агрегатных состояний происходит автоматически при каждой вставке новых данных в исходную таблицу. Использование материализованных представлений позволяет оптимизировать время выполнения сложных запросов (с объединениями и агрегатами), обращающихся к большому количеству записей — за счет предварительного вычисления итоговых значений сокращается объем данных, которые необходимо обработать для выполнения запросов SELECT.

Создание

Обзор синтаксиса

Чтобы создать материализованное представление, используйте запрос CREATE MATERIALIZED VIEW. Общий синтаксис:

CREATE MATERIALIZED VIEW [IF NOT EXISTS] [<db_name>.]<mat_view_name> [ON CLUSTER <cluster_name>]
[TO [<db_name>.]<target_table>]
[ENGINE = <engine>]
[POPULATE]
AS SELECT
    ...
FROM [<db_name>.]<source_table>
...;

Для описания материализованного представления используются следующие выражения.

TO

Целевая таблица (target table), в которую будут сохраняться данные из материализованного представления

ENGINE

Движок внутренней таблицы материализованного представления, где будут храниться агрегированные данные, если отдельная целевая таблица не была заранее создана и явно указана через выражение TO в описании материализованного представления

POPULATE

Указывает, что при создании материализованного представления в него будут добавлены данные, уже содержащиеся в исходной таблице. Использовать POPULATE не рекомендуется, так как в представление не попадут данные, добавляемые в исходную таблицу во время создания представления. Кроме того, ключевое слово POPULATE не указывается, если описание материализованного представления включает выражение TO — в этом случае представление будет обрабатывать и сохранять только данные, добавляемые в исходную таблицу после создания представления

AS SELECT …​ FROM …​

Запрос, который считывает новые данные, вставляемые в исходную таблицу, и определяет, как эти данные преобразовывать для загрузки в материализованное представление (или целевую таблицу, если она указана). Запрос SELECT может содержать выражения DISTINCT, GROUP BY, ORDER BY, LIMIT — соответствующие преобразования будут выполняться независимо для каждого блока вставляемых данных. Например, при наличии GROUP BY данные будут агрегироваться при вставке в рамках только одного блока вставляемых данных. Далее данные не будут доагрегированы. Исключением является использование табличного движка, указанного в описании представления через ENGINE, который выполняет агрегацию данных самостоятельно (например, SummingMergeTree).

Изменить запрос SELECT после создания материализованного представления можно с помощью запроса ALTER TABLE … MODIFY QUERY — но учитывайте, что в настоящее время этот тип запроса является экспериментальным и имеет некоторые ограничения

ПРИМЕЧАНИЕ

Изменения существующих данных в исходной таблице (например, обновление, удаление и т.д.) не влияют на материализованное представление.

В ADQM материализованные представления выглядят так же, как обычные таблицы. Например, они включаются в результат запроса SHOW TABLES.

Чтобы удалить материализованное представление, выполните запрос DROP VIEW или DROP TABLE.

Пример

В примере ниже показано, как создать материализованное представление для предварительной агрегации данных, чтобы ускорить последующие выполнения запросов SELECT.

  1. Создайте таблицу, которая будет хранить исходные данные о просмотрах страниц:

    CREATE TABLE page_views
    (
        time DateTime,
        page_name String,
        time_on_page UInt64
    )
    ENGINE = MergeTree
    ORDER BY time;
  2. Создайте целевую таблицу, в которую будут записываться количество просмотров и среднее время просмотра страниц по месяцам:

    CREATE TABLE page_views_monthly
    (
        page_name String,
        month Date,
        page_view_count UInt64,
        avg_time_on_page AggregateFunction(avg, UInt64)
    )
    ENGINE = SummingMergeTree()
    ORDER BY (page_name, month);

    Для колонки со средним значением времени просмотра страниц используется тип данных AggregateFunction, который позволяет хранить частично агрегированные данные. Табличный движок SummingMergeTree, который используется в примере, требует этот тип данных для всех агрегатов, кроме sum и count. Для работы с материализованными представлениями можно использовать и другие движки таблиц. Например, движок AggregatingMergeTree — этот движок работает только с агрегатными функциями (то есть если бы в запросе выше использовался движок AggregatingMergeTree, для колонки page_view_count также нужно было бы использовать тип AggregateFunction).

  3. Создайте материализованное представление:

    CREATE MATERIALIZED VIEW page_views_monthly_mv
    TO page_views_monthly
    AS SELECT
        toDate(toStartOfMonth(time)) AS month,
        page_name,
        count() AS page_view_count,
        avgState(time_on_page) AS avg_time_on_page
    FROM page_views
    GROUP BY page_name, month
    ORDER BY page_name, month;

    При вставке данных в исходную таблицу page_views материализованное представление преобразует вставляемые данные, как указано в запросе SELECT, и поместит результат в целевую таблицу page_views_monthly. Для сохранения промежуточных агрегатных состояний в столбец типа AggregateFunction (avg_time_on_page) в запросе SELECT должна использоваться соответствующая агрегатная функция с суффиксом State (avgState).

    Если на этом этапе выполнить запрос SHOW TABLES, вывод на экран будет включать созданные таблицы и материализованное представление:

    ┌─name──────────────────┐
    │ page_views            │
    │ page_views_monthly    │
    │ page_views_monthly_mv │
    └───────────────────────┘
  4. Заполните исходную таблицу тестовыми данными. Например, сгенерируйте случайным образом 1 миллион просмотров для 4 страниц:

    INSERT INTO page_views SELECT
        toDate('2024-01-01 00:00:00') + (rand() % 182) AS time,
        ['landing', 'products', 'docs', 'about'][toInt32(randBinomial(4, 0.01)) + 1],
        rand() % 15
    FROM numbers(1000000);
  5. Сравните скорость выполнения запроса SELECT при выборке данных из исходной таблицы с необработанными входящими данными и таблицы с предварительно агрегированными данными по месяцам.

     
    Сделайте выборку данных из таблицы page_views:

    SELECT
        page_name,
        toDate(toStartOfMonth(time)) AS month,
        count() AS page_view_count,
        round(avg(time_on_page), 2) AS avg_time_on_page
    FROM page_views
    GROUP BY page_name, month
    ORDER BY page_name, month;
        ┌─page_name─┬──────month─┬─page_view_count─┬─avg_time_on_page─┐
     1. │ about     │ 2024-04-01 │               1 │                3 │
     2. │ docs      │ 2024-01-01 │             104 │             7.24 │
     3. │ docs      │ 2024-02-01 │             104 │             7.24 │
     4. │ docs      │ 2024-03-01 │             104 │             7.47 │
     5. │ docs      │ 2024-04-01 │              83 │             7.02 │
     6. │ docs      │ 2024-05-01 │             107 │             6.96 │
     7. │ docs      │ 2024-06-01 │             100 │             7.09 │
     8. │ landing   │ 2024-01-01 │          163232 │             6.99 │
     9. │ landing   │ 2024-02-01 │          153277 │                7 │
    10. │ landing   │ 2024-03-01 │          163137 │                7 │
    11. │ landing   │ 2024-04-01 │          158572 │             6.99 │
    12. │ landing   │ 2024-05-01 │          164388 │             6.99 │
    13. │ landing   │ 2024-06-01 │          158005 │                7 │
    14. │ products  │ 2024-01-01 │            6549 │             7.09 │
    15. │ products  │ 2024-02-01 │            6106 │                7 │
    16. │ products  │ 2024-03-01 │            6627 │             6.98 │
    17. │ products  │ 2024-04-01 │            6565 │             7.02 │
    18. │ products  │ 2024-05-01 │            6596 │             6.89 │
    19. │ products  │ 2024-06-01 │            6343 │             7.02 │
        └───────────┴────────────┴─────────────────┴──────────────────┘
    
    19 rows in set. Elapsed: 0.023 sec. Processed 1.00 million rows, 28.04 MB (43.07 million rows/s., 1.21 GB/s.)

    Сделайте выборку из таблицы page_views_monthly — чтение данных из этой таблицы выполнится в 7 раз быстрее, чем из исходной таблицы (на большем объеме данных разница будет еще более заметна). Обратите внимание, в запросе SELECT используется та же агрегатная функция, что и при вставке данных, но с суффиксом Merge — это необходимо, чтобы объединить множество промежуточных агрегатных состояний и вернуть полную агрегацию данных (см. AggregateFunction):

    SELECT
        page_name,
        month,
        sum(page_view_count) AS page_view_count,
        round(avgMerge(avg_time_on_page), 2) AS avg_time_on_page
    FROM page_views_monthly
    GROUP BY page_name, month
    ORDER BY page_name, month;
        ┌─page_name─┬──────month─┬─page_view_count─┬─avg_time_on_page─┐
     1. │ about     │ 2024-04-01 │               1 │                3 │
     2. │ docs      │ 2024-01-01 │             104 │             7.24 │
     3. │ docs      │ 2024-02-01 │             104 │             7.24 │
     4. │ docs      │ 2024-03-01 │             104 │             7.47 │
     5. │ docs      │ 2024-04-01 │              83 │             7.02 │
     6. │ docs      │ 2024-05-01 │             107 │             6.96 │
     7. │ docs      │ 2024-06-01 │             100 │             7.09 │
     8. │ landing   │ 2024-01-01 │          163232 │             6.99 │
     9. │ landing   │ 2024-02-01 │          153277 │                7 │
    10. │ landing   │ 2024-03-01 │          163137 │                7 │
    11. │ landing   │ 2024-04-01 │          158572 │             6.99 │
    12. │ landing   │ 2024-05-01 │          164388 │             6.99 │
    13. │ landing   │ 2024-06-01 │          158005 │                7 │
    14. │ products  │ 2024-01-01 │            6549 │             7.09 │
    15. │ products  │ 2024-02-01 │            6106 │                7 │
    16. │ products  │ 2024-03-01 │            6627 │             6.98 │
    17. │ products  │ 2024-04-01 │            6565 │             7.02 │
    18. │ products  │ 2024-05-01 │            6596 │             6.89 │
    19. │ products  │ 2024-06-01 │            6343 │             7.02 │
        └───────────┴────────────┴─────────────────┴──────────────────┘
    
    19 rows in set. Elapsed: 0.003 sec.

Изменение

Запрос SELECT, который был указан при создании материализованного представления, можно изменить с помощью выражения ALTER TABLE…MODIFY QUERY. Эта команда не меняет структуру и описания столбцов внутренней таблицы материализованного представления, поэтому не рекомендуется ее использование для изменения материализованных представлений, созданных без указания целевой таблицы через выражение TO.

Перед выполнением команды ALTER TABLE…MODIFY QUERY, активируйте опцию: SET allow_experimental_alter_materialized_view_structure=1.

Пример

  1. С помощью выражения ALTER TABLE…​ADD COLUMN добавьте столбец browser в исходную таблицу page_views и целевую таблицу page_views_monthly (столбцы материализованного представления и целевой таблицы не обязательно должны совпадать, поэтому изменение структуры целевой таблицы не нарушит вставку данных):

    ALTER TABLE page_views ADD COLUMN browser String;
    ALTER TABLE page_views_monthly ADD COLUMN browser String after month,
    MODIFY ORDER BY (page_name, month, browser);
  2. Вставьте данные в исходную таблицу:

    INSERT INTO page_views SELECT
        toDate('2024-01-01 00:00:00') + (rand() % 182) AS time,
        ['landing', 'products', 'docs', 'about'][toInt32(randBinomial(4, 0.01)) + 1],
        rand() % 15,
        ['firefox', 'safari', 'chrome'][(number % 3) + 1]
    FROM numbers(1000000);

    Если на этом этапе сделать выборку данных из целевой таблицы, столбец browser будет пустым:

    SELECT
        page_name,
        month,
        browser,
        sum(page_view_count) AS page_view_count,
        round(avgMerge(avg_time_on_page), 2) AS avg_time_on_page
    FROM page_views_monthly
    GROUP BY page_name, month, browser
    ORDER BY page_name, month ASC, browser;
        ┌─page_name─┬──────month─┬─browser─┬─page_view_count─┬─avg_time_on_page─┐
     1. │ about     │ 2024-04-01 │         │               1 │                3 │
     2. │ about     │ 2024-06-01 │         │               1 │                7 │
     3. │ docs      │ 2024-01-01 │         │             204 │             7.42 │
     4. │ docs      │ 2024-02-01 │         │             176 │             7.12 │
     5. │ docs      │ 2024-03-01 │         │             200 │             7.36 │
     6. │ docs      │ 2024-04-01 │         │             188 │              7.1 │
     7. │ docs      │ 2024-05-01 │         │             210 │             6.78 │
     8. │ docs      │ 2024-06-01 │         │             199 │             6.89 │
     9. │ landing   │ 2024-01-01 │         │          326955 │                7 │
    10. │ landing   │ 2024-02-01 │         │          305943 │                7 │
    11. │ landing   │ 2024-03-01 │         │          326323 │                7 │
    12. │ landing   │ 2024-04-01 │         │          316787 │             6.99 │
    13. │ landing   │ 2024-05-01 │         │          327745 │             6.99 │
    14. │ landing   │ 2024-06-01 │         │          317279 │             7.01 │
    15. │ products  │ 2024-01-01 │         │           13294 │                7 │
    16. │ products  │ 2024-02-01 │         │           12319 │             6.97 │
    17. │ products  │ 2024-03-01 │         │           13230 │             7.05 │
    18. │ products  │ 2024-04-01 │         │           12990 │             7.02 │
    19. │ products  │ 2024-05-01 │         │           13219 │              6.9 │
    20. │ products  │ 2024-06-01 │         │           12737 │             7.02 │
        └───────────┴────────────┴─────────┴─────────────────┴──────────────────┘
  3. Чтобы данные из нового столбца исходной таблицы попадали в соответствующий столбец целевой таблицы, необходимо изменить запрос SELECT материализованного представления. Для этого выполните команду ALTER TABLE…​MODIFY QUERY:

    SET allow_experimental_alter_materialized_view_structure=1;
    ALTER TABLE page_views_monthly_mv
    MODIFY QUERY
    SELECT
        toDate(toStartOfMonth(time)) AS month,
        page_name,
        browser,
        count() AS page_view_count,
        avgState(time_on_page) AS avg_time_on_page
    FROM page_views
    GROUP BY page_name, month, browser
    ORDER BY page_name, month, browser;
  4. Добавьте еще раз данные в исходную таблицу:

    INSERT INTO page_views SELECT
        toDate('2024-01-01 00:00:00') + (rand() % 182) AS time,
        ['landing', 'products', 'docs', 'about'][toInt32(randBinomial(4, 0.01)) + 1],
        rand() % 15,
        ['firefox', 'safari', 'chrome'][(number % 3) + 1]
    FROM numbers(1000000);

    Убедитесь, что данные из столбца browser теперь учитываются в целевой таблице:

    SELECT
        page_name,
        month,
        browser,
        sum(page_view_count) AS page_view_count,
        round(avgMerge(avg_time_on_page), 2) AS avg_time_on_page
    FROM page_views_monthly
    GROUP BY page_name, month, browser
    ORDER BY page_name, month ASC, browser;
        ┌─page_name─┬──────month─┬─browser─┬─page_view_count─┬─avg_time_on_page─┐
     1. │ about     │ 2024-02-01 │ safari  │               1 │               11 │
     2. │ about     │ 2024-04-01 │         │               1 │                3 │
     3. │ about     │ 2024-04-01 │ firefox │               1 │               10 │
     4. │ about     │ 2024-04-01 │ safari  │               1 │               12 │
     5. │ about     │ 2024-05-01 │ chrome  │               1 │                7 │
     6. │ about     │ 2024-05-01 │ firefox │               1 │               10 │
     7. │ about     │ 2024-06-01 │         │               1 │                7 │
     8. │ about     │ 2024-06-01 │ safari  │               2 │               12 │
     9. │ docs      │ 2024-01-01 │         │             204 │             7.42 │
    10. │ docs      │ 2024-01-01 │ chrome  │              40 │             7.08 │
    11. │ docs      │ 2024-01-01 │ firefox │              35 │             7.46 │
    12. │ docs      │ 2024-01-01 │ safari  │              37 │             7.46 │
    13. │ docs      │ 2024-02-01 │         │             176 │             7.12 │
    14. │ docs      │ 2024-02-01 │ chrome  │              24 │             7.83 │
    15. │ docs      │ 2024-02-01 │ firefox │              32 │             8.28 │
    16. │ docs      │ 2024-02-01 │ safari  │              33 │             8.64 │
    17. │ docs      │ 2024-03-01 │         │             200 │             7.36 │
    18. │ docs      │ 2024-03-01 │ chrome  │              38 │             7.24 │
    19. │ docs      │ 2024-03-01 │ firefox │              34 │             5.29 │
    20. │ docs      │ 2024-03-01 │ safari  │              29 │             7.59 │
    21. │ docs      │ 2024-04-01 │         │             188 │              7.1 │
    22. │ docs      │ 2024-04-01 │ chrome  │              37 │             6.51 │
    23. │ docs      │ 2024-04-01 │ firefox │              27 │                8 │
    24. │ docs      │ 2024-04-01 │ safari  │              31 │             7.06 │
    25. │ docs      │ 2024-05-01 │         │             210 │             6.78 │
    26. │ docs      │ 2024-05-01 │ chrome  │              27 │             7.78 │
    27. │ docs      │ 2024-05-01 │ firefox │              30 │             7.03 │
    28. │ docs      │ 2024-05-01 │ safari  │              27 │             8.26 │
    29. │ docs      │ 2024-06-01 │         │             199 │             6.89 │
    30. │ docs      │ 2024-06-01 │ chrome  │              38 │             5.82 │
    31. │ docs      │ 2024-06-01 │ firefox │              44 │             7.32 │
    32. │ docs      │ 2024-06-01 │ safari  │              27 │             7.04 │
    33. │ landing   │ 2024-01-01 │         │          326955 │                7 │
    34. │ landing   │ 2024-01-01 │ chrome  │           54625 │             6.99 │
    35. │ landing   │ 2024-01-01 │ firefox │           54441 │             7.01 │
    36. │ landing   │ 2024-01-01 │ safari  │           54428 │                7 │
    37. │ landing   │ 2024-02-01 │         │          305943 │                7 │
    38. │ landing   │ 2024-02-01 │ chrome  │           51192 │             7.03 │
    39. │ landing   │ 2024-02-01 │ firefox │           51085 │             7.01 │
    40. │ landing   │ 2024-02-01 │ safari  │           50756 │             6.98 │
    41. │ landing   │ 2024-03-01 │         │          326323 │                7 │
    42. │ landing   │ 2024-03-01 │ chrome  │           54427 │             6.98 │
    43. │ landing   │ 2024-03-01 │ firefox │           54567 │             7.01 │
    44. │ landing   │ 2024-03-01 │ safari  │           54644 │             7.04 │
    45. │ landing   │ 2024-04-01 │         │          316787 │             6.99 │
    46. │ landing   │ 2024-04-01 │ chrome  │           52779 │                7 │
    47. │ landing   │ 2024-04-01 │ firefox │           52752 │                7 │
    48. │ landing   │ 2024-04-01 │ safari  │           53087 │             6.97 │
    49. │ landing   │ 2024-05-01 │         │          327745 │             6.99 │
    50. │ landing   │ 2024-05-01 │ chrome  │           54246 │                7 │
    51. │ landing   │ 2024-05-01 │ firefox │           54488 │             7.02 │
    52. │ landing   │ 2024-05-01 │ safari  │           54815 │                7 │
    53. │ landing   │ 2024-06-01 │         │          317279 │             7.01 │
    54. │ landing   │ 2024-06-01 │ chrome  │           52955 │             7.01 │
    55. │ landing   │ 2024-06-01 │ firefox │           52806 │                7 │
    56. │ landing   │ 2024-06-01 │ safari  │           52665 │             6.99 │
    57. │ products  │ 2024-01-01 │         │           13294 │                7 │
    58. │ products  │ 2024-01-01 │ chrome  │            2175 │             7.11 │
    59. │ products  │ 2024-01-01 │ firefox │            2235 │             7.05 │
    60. │ products  │ 2024-01-01 │ safari  │            2149 │             6.99 │
    61. │ products  │ 2024-02-01 │         │           12319 │             6.97 │
    62. │ products  │ 2024-02-01 │ chrome  │            2113 │             7.07 │
    63. │ products  │ 2024-02-01 │ firefox │            2055 │             6.91 │
    64. │ products  │ 2024-02-01 │ safari  │            1984 │             6.88 │
    65. │ products  │ 2024-03-01 │         │           13230 │             7.05 │
    66. │ products  │ 2024-03-01 │ chrome  │            2254 │             6.84 │
    67. │ products  │ 2024-03-01 │ firefox │            2206 │             6.98 │
    68. │ products  │ 2024-03-01 │ safari  │            2250 │             7.21 │
    69. │ products  │ 2024-04-01 │         │           12990 │             7.02 │
    70. │ products  │ 2024-04-01 │ chrome  │            2017 │              6.9 │
    71. │ products  │ 2024-04-01 │ firefox │            2190 │             6.95 │
    72. │ products  │ 2024-04-01 │ safari  │            2033 │             6.99 │
    73. │ products  │ 2024-05-01 │         │           13219 │              6.9 │
    74. │ products  │ 2024-05-01 │ chrome  │            2240 │             6.97 │
    75. │ products  │ 2024-05-01 │ firefox │            2170 │             7.13 │
    76. │ products  │ 2024-05-01 │ safari  │            2205 │             7.05 │
    77. │ products  │ 2024-06-01 │         │           12737 │             7.02 │
    78. │ products  │ 2024-06-01 │ chrome  │            2105 │             7.09 │
    79. │ products  │ 2024-06-01 │ firefox │            2135 │             6.93 │
    80. │ products  │ 2024-06-01 │ safari  │            2129 │             6.95 │
        └───────────┴────────────┴─────────┴─────────────────┴──────────────────┘

Каскадные материализованные представления

Каскадные материализованные представления (cascading materialized view) — это цепочка материализованных представлений, где одно материализованное представление служит источником данных для другого (то есть данные, преобразованные одним материализованным представлением, перенаправляются в другое материализованное представление для последующего преобразования). Использование каскадных материализованных представлений может быть полезно для повышения эффективности ETL-процессов (например, когда необходимо выполнить многоуровневую агрегацию данных перед записью в конечное хранилище).

Пример

Для следующего уровня агрегирования (по годам) данных о просмотрах страниц из примера выше создайте второе материализованное представление, которое будет читать уже агрегированные (по месяцам) данные из первого материализованного представления — это будет пример каскада материализованных представлений:

  1. Создайте целевую таблицу, в которую будут записываться количество просмотров и среднее время просмотра страниц по годам:

    CREATE TABLE page_views_yearly
    (
        page_name String,
        year Date,
        page_view_count UInt64,
        avg_time_on_page AggregateFunction(avg, UInt64)
    )
    ENGINE = SummingMergeTree()
    ORDER BY (page_name, year);
  2. Создайте материализованное представление, исходной таблицей которого является целевая таблица материализованного представления page_views_monthly_mv — page_views_monthly:

    CREATE MATERIALIZED VIEW page_views_yearly_mv
    TO page_views_yearly
    AS SELECT
        page_name,
        toStartOfYear(month) AS year,
        sum(page_view_count) AS page_view_count,
        avgMergeState(avg_time_on_page) AS avg_time_on_page
    FROM page_views_monthly
    GROUP BY page_name, year
    ORDER BY page_name, year;

    Обратите внимание, здесь в запросе SELECT для столбца avg_time_on_page типа AggregateFunction используется агрегатная функция с суффиксом MergeState — этот комбинатор объединяет промежуточные агрегатные состояния как Merge, но возвращает не итоговые значения агрегации, а промежуточные агрегатные состояния как State.

    Если на этом этапе запросить данные из таблицы page_views_yearly, она будет пустая, так как ее заполнение будет выполняться только при поступлении новых данных в таблицу page_views_monthly.

  3. Чтобы использовать уже имеющиеся в таблице page_views_monthly данные для заполнения page_views_yearly, выполните запрос:

    INSERT INTO page_views_yearly
    SELECT
        page_name,
        toStartOfYear(month) AS year,
        sum(page_view_count) AS page_view_count,
        avgMergeState(avg_time_on_page) AS avg_time_on_page
    FROM page_views_monthly
    GROUP BY page_name, year;

    Проверьте, что информация о просмотрах страниц, агрегированная по годам, добавлена в таблицу page_views_yearly:

    SELECT
        page_name,
        toYear(year) AS year,
        sum(page_view_count) AS page_view_count,
        round(avgMerge(avg_time_on_page), 2) AS avg_time_on_page
    FROM page_views_yearly
    GROUP BY page_name, year
    ORDER BY page_name, year;
       ┌─page_name─┬─year─┬─page_view_count─┬─avg_time_on_page─┐
    1. │ about     │ 2024 │               9 │             9.33 │
    2. │ docs      │ 2024 │            1767 │             7.16 │
    3. │ landing   │ 2024 │         2881790 │                7 │
    4. │ products  │ 2024 │          116434 │                7 │
       └───────────┴──────┴─────────────────┴──────────────────┘

    При последующем добавлении новых данных в исходную таблицу page_views оба материализованных представления будут автоматически срабатывать. Чтобы убедиться, повторите запросы на вставку данных и чтение таблицы page_views_yearly:

    INSERT INTO page_views SELECT
        toDate('2024-01-01 00:00:00') + (rand() % 730) AS time,
        ['landing', 'products', 'docs', 'about'][toInt32(randBinomial(4, 0.01)) + 1],
        rand() % 15,
        ['firefox', 'safari', 'chrome'][(number % 3) + 1]
    FROM numbers(1000000);
    SELECT
        page_name,
        toYear(year) AS year,
        sum(page_view_count) AS page_view_count,
        round(avgMerge(avg_time_on_page), 2) AS avg_time_on_page
    FROM page_views_yearly
    GROUP BY page_name, year
    ORDER BY page_name, year;
       ┌─page_name─┬─year─┬─page_view_count─┬─avg_time_on_page─┐
    1. │ about     │ 2024 │              12 │                8 │
    2. │ about     │ 2025 │               1 │               11 │
    3. │ docs      │ 2024 │            2036 │             7.14 │
    4. │ docs      │ 2025 │             304 │             7.27 │
    5. │ landing   │ 2024 │         3363344 │                7 │
    6. │ landing   │ 2025 │          479473 │                7 │
    7. │ products  │ 2024 │          135515 │                7 │
    8. │ products  │ 2025 │           19315 │             7.02 │
       └───────────┴──────┴─────────────────┴──────────────────┘
Нашли ошибку? Выделите текст и нажмите Ctrl+Enter чтобы сообщить о ней