Я создал таблицу:

CREATE TABLE results
(
    id UUID,
    date_time DateTime,
    item_id UInt32,
    value UInt16
) ENGINE = MergeTree()
PARTITION BY toYYYYMMDD(date_time)
ORDER BY (date_time, item_id);

И я хочу создать материализованное представление для хранения почасовых данных гистограммы для значения. Например;

Я ожидаю такой вывод:

toStartOfHour          item_id    value    count
2019-12-18 00:00:00    1          0        4       /* number of rows with value between 0 and 100 and date_time between 2019-12-18 00:00:00 and 2019-12-18 01:00:00 */
2019-12-18 00:00:00    1          100      7       /* number of rows with value between 100 and 200 and date_time between 2019-12-18 00:00:00 and 2019-12-18 01:00:00 */

Количество строк с value между 100 и 0 и date_time между 2019-12-18 00:00:00 и 2019-12-18 01:00:00. Я пробовал что-то вроде этого:

CREATE MATERIALIZED VIEW results_histogram_by_hour
ENGINE = AggregatingMergeTree()
PARTITION BY toYYYYMMDD(date_time)
ORDER BY (date_time, item_id)
POPULATE
AS SELECT toStartOfHour(date_time) AS date_time,
          item_id,
          multiply(floor(value / 100), 100) AS value,
          countState() AS count
FROM results
GROUP BY date_time,
         item_id,
         value;

Это материализованное определение представления работает при заполнении. Но со временем и новыми рядами все становится не так. Как неправильно? Я не знаю. Я не мог найти образец.

Я не уверен, нашел ли я ошибку в clickhouse или я делаю что-то не так.

Правильно ли мое материализованное определение представления?

1
Gokhan Sari 18 Дек 2019 в 21:20

2 ответа

AggregatingMT использует порядок по (первичному ключу), так как Dimensions все остальные столбцы: Metrics. Если столбец метрики не имеет функции состояния, он будет рассчитан или свернут ANY

CREATE table results_histogram_by_hour
(date_time DateTime,
 item_id UInt32,
 value UInt16,
 count AggregateFunction(count) 
) ENGINE = AggregatingMergeTree() 
PARTITION BY toYYYYMMDD(date_time) 
ORDER BY (date_time, item_id)

insert into results_histogram_by_hour 
select toStartOfHour(now()) date_time,
       1 item_id,
       1 value,
       countState()
group by date_time, item_id, value;

insert into results_histogram_by_hour 
select toStartOfHour(now()) date_time,
       1 item_id,
      99 value,
       countState()
group by date_time, item_id, value;

optimize table results_histogram_by_hour final;

select * from results_histogram_by_hour;

┌───────────date_time─┬─item_id─┬─value─┬─count─┐
│ 2019-12-18 21:00:00 │       1 │     1 │       │
└─────────────────────┴─────────┴───────┴───────┘

ORDER BY (date_time, item_id , value)
┌───────────date_time─┬─item_id─┬─value─┬─count─┐
│ 2019-12-18 21:00:00 │       1 │     1 │       │
│ 2019-12-18 21:00:00 │       1 │    99 │       │
└─────────────────────┴─────────┴───────┴───────┘

Если вам не нравится идея длинного / широкого / тяжелого индекса (PRIMARY KEY), можно использовать другой набор столбцов для ORDERBY / PRIMARYKEY. Все ДВИГАТЕЛИ используют набор столбцов ORDERBY для слияния / свертывания.

2
Denny Crane 19 Дек 2019 в 00:41

Другой подход

https://clickhouse.yandex/docs/en/operations/table_engines/summingmergetree/#nested-structures

SummingMergeTree может суммировать значения в массивах K / V (столбцы должны быть названы ... Карта - значение Карта )

CREATE TABLE results
(
    id UInt64,
    date_time DateTime,
    item_id UInt32,
    value UInt16
) ENGINE = MergeTree()
PARTITION BY toYYYYMMDD(date_time)
ORDER BY (date_time, item_id);


insert into results 
select number,
       now(),
       number%7 item_id,
       number%9957 value
from numbers(10000);


CREATE MATERIALIZED VIEW results_histogram_by_hour
ENGINE = SummingMergeTree()
PARTITION BY toYYYYMMDD(date_time)
ORDER BY (date_time, item_id) POPULATE AS
SELECT
    date_time,
    item_id,
    groupArray(value) AS `valueMap.bin`,
    groupArray(cnt) AS `valueMap.cnt`
FROM
(
    SELECT
        toStartOfHour(date_time) AS date_time,
        item_id,
        intDiv(value, 1000) AS value,
        sum(toUInt64(1)) AS cnt
    FROM results
    GROUP BY
        date_time,
        item_id,
        value
)
GROUP BY
    date_time,
    item_id


insert into results 
select number,
       now(),
       number%7 item_id,
       number%9957 value
from numbers(10000);

SELECT *
FROM results_histogram_by_hour
WHERE item_id = 4

─item_id─┬─valueMap.bin──────────┬─valueMap.cnt──────────────────────────────┐
       4 │ [0,7,6,1,5,2,3,4,8,9] │ [149,143,143,143,143,142,143,143,143,136] │
─────────┴───────────────────────┴───────────────────────────────────────────┘
─item_id─┬─valueMap.bin──────────┬─valueMap.cnt──────────────────────────────┐
       4 │ [0,7,6,1,5,2,3,4,8,9] │ [149,143,143,143,143,142,143,143,143,136] │
─────────┴───────────────────────┴───────────────────────────────────────────┘    

SELECT
    date_time,
    item_id,
    sumMap(valueMap.bin, valueMap.cnt)
FROM results_histogram_by_hour
WHERE item_id = 4
GROUP BY
    date_time,
    item_id

─item_id─┬─sumMap(valueMap.bin, valueMap.cnt)────────────────────────────────┐
       4 │ ([0,1,2,3,4,5,6,7,8,9],[298,286,284,286,286,286,286,286,286,272]) │
─────────┴───────────────────────────────────────────────────────────────────┘

optimize table results_histogram_by_hour final;

SELECT *
FROM results_histogram_by_hour
WHERE item_id = 4

─item_id─┬─valueMap.bin──────────┬─valueMap.cnt──────────────────────────────┐
       4 │ [0,1,2,3,4,5,6,7,8,9] │ [298,286,284,286,286,286,286,286,286,272] │
─────────┴───────────────────────┴───────────────────────────────────────────┘
1
Denny Crane 19 Дек 2019 в 04:02