Помимо базовых операций подключения, агрегации и группировки, YMatrix также предоставляет множество расширенных аналитических функций, таких как оконные функции, CTE (Common Table Expression), упорядоченные агрегатные функции, часто используемые временные функции и т.д. В этом разделе вы ознакомитесь с методами расширенных запросов, commonly используемых в YMatrix.
Ниже приведен пример статистики использования дискового пространства, демонстрирующий выполнение расширенных запросов в YMatrix. В образце метрической таблицы disk мы спроектировали поля скорости чтения и записи диска, используя механизм хранения MARS2.
Таблицы MARS2 зависят от расширения matrixts. Перед созданием таблицы необходимо сначала создать расширение в базе данных с использованием этого механизма хранения. Это расширение действует на уровне базы данных и не требует повторного создания.
CREATE EXTENSION matrixts;
Создайте метрическую таблицу disk.
CREATE TABLE disk (
time timestamp with time zone,
tag_id int,
read float,
write float
)
USING mars2
DISTRIBUTED BY (tag_id);
После успешного создания таблицы MARS2 необходимо дополнительно создать индекс типа mars2_btree, чтобы обеспечить нормальную запись и чтение.
CREATE INDEX idx_mars2 ON disk
USING mars2_btree(tag_id);
Дополнительную информацию о MARS2 см. в разделе Storage Engine
Оконные функции в основном используются для обработки сравнительно сложных сценариев статистического анализа отчетов. «Окно» определяет набор данных, в котором выполняются агрегированные запросы между строками, связанными с текущей строкой. Различие между оконными функциями и другими SQL-функциями заключается в наличии предложения OVER. Если функция содержит предложение OVER, то это оконная функция. Вы можете понять разницу между оконными функциями и обычными агрегатными функциями по следующей таблице.
| Обычные агрегатные функции | Оконные функции | |
|---|---|---|
| Вывод | Одна запись | Несколько записей |
| Функции | max(), min(), count(), sum() и т.д. | avg(), sum(), rank() и т.д. |
| Использование | обычно используется совместно с предложением GROUP BY | используется совместно с предложением OVER. Как правило, предложение OVER записывается сразу после имени и параметров оконной функции. В предложении OVER обычно можно использовать три подпредложения: PARTITION BY, ORDER BY и ROWS BETWEEN. Использование только подпредложения PARTITION BY формирует статическое окно, размер и положение которого не изменяются; если помимо PARTITION BY используются одно или два из подпредложений ORDER BY и ROWS BETWEEN, формируется скользящее окно, то есть размер и положение окна постоянно меняются. |
Вы можете потренироваться в использовании часто применяемых оконных функций на следующих примерах.
Используя вложенную функцию SUM, можно рассчитать накопительную сумму.
Следующий SQL-запрос вычисляет накопительную сумму чтения и записи диска с tag_id = 1 за период с 2021-04-10 21:00:00 до 2021-04-10 21:00:10:
ymatrix=# SELECT time,
sum(sum(read)) OVER (ORDER BY time) AS read,
sum(sum(write)) OVER (ORDER BY time) AS write
FROM disk
WHERE time BETWEEN '2021-04-10 21:00:00'::timestamp AND '2021-04-10 21:00:10'::timestamp
AND tag_id = 1
GROUP BY time
ORDER BY time;
time | read | write
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2021-04-10 21:00:00+08 | 81.07 | 73.3
2021-04-10 21:00:01+08 | 110.63 | 121.77
2021-04-10 21:00:02+08 | 202.12 | 201.36
2021-04-10 21:00:03+08 | 263.74 | 257.88
2021-04-10 21:00:04+08 | 361.6 | 299.3
2021-04-10 21:00:05+08 | 394.49 | 327.33000000000004
2021-04-10 21:00:06+08 | 438.3 | 334.98
2021-04-10 21:00:07+08 | 523.35 | 431.39
2021-04-10 21:00:08+08 | 583.15 | 461.84
2021-04-10 21:00:09+08 | 609.01 | 533.03
2021-04-10 21:00:10+08 | 669.52 | 535.9
(11 rows)
Скользящее среднее используется для расчета среднего значения за предыдущие n строк.
Следующий SQL-запрос вычисляет средние значения чтения и записи каждые 10 секунд с 2021-04-10 21:00:00 до 21:01:00 (для первых 9 данных рассчитывается среднее значение количества строк, удовлетворяющих условию):
ymatrix=# SELECT time,
round(AVG(read) OVER(ORDER BY time ROWS BETWEEN 9 PRECEDING AND CURRENT ROW)) AS read,
round(AVG(write) OVER(ORDER BY time ROWS BETWEEN 9 PRECEDING AND CURRENT ROW)) AS write
FROM disk
WHERE time BETWEEN '2021-04-10 21:00:00'::timestamp AND '2021-04-10 21:01:00'::timestamp
AND tag_id = 1
ORDER BY time DESC;
time | read | write
------------------------+-------------------------------------
2021-04-10 21:01:00+08 | 57 | 57
2021-04-10 21:00:59+08 | 49 | 60
2021-04-10 21:00:58+08 | 52 | 56
2021-04-10 21:00:57+08 | 51 | 57
2021-04-10 21:00:56+08 | 53 | 65
2021-04-10 21:00:55+08 | 48 | 64
2021-04-10 21:00:54+08 | 49 | 64
2021-04-10 21:00:53+08 | 47 | 54
2021-04-10 21:00:52+08 | 44 | 54
2021-04-10 21:00:51+08 | 41 | 56
......
Приращение обычно используется для расчета увеличения или уменьшения монотонной последовательности, а также может быть просто использовано для вычисления изменения относительно предыдущего значения.
Следующий запрос вычисляет изменение значения чтения диска с tag_id = 1. С 2021-04-10 21:00:00 до 21:01:00 положительное число означает рост по сравнению с предыдущей секундой, отрицательное — снижение:
ymatrix=# SELECT
time,
(
CASE WHEN lag(read) OVER (ORDER BY time) IS NULL THEN NULL
ELSE round(read - lag(read) OVER (ORDER BY time))
END
) AS read
FROM disk
WHERE time BETWEEN '2021-04-10 21:00:00'::timestamp AND '2021-04-10 21:01:00'::timestamp
AND tag_id = 1
ORDER BY time;
time | read
---------------------------------------------------
2021-04-10 21:00:00+08 |
2021-04-10 21:00:01+08 | -52
2021-04-10 21:00:02+08 | 62
2021-04-10 21:00:03+08 | -30
2021-04-10 21:00:04+08 | 36
2021-04-10 21:00:05+08 | -65
2021-04-10 21:00:06+08 | 11
2021-04-10 21:00:07+08 | 41
2021-04-10 21:00:08+08 | -25
2021-04-10 21:00:09+08 | -34
......
На основе приращения деление его на интервал времени позволяет получить темп роста (так как в примере данные собираются раз в секунду, результаты аналогичны приращению):
ymatrix=# SELECT
time,
(
CASE WHEN lag(read) OVER (ORDER BY time) IS NULL THEN NULL
ELSE round(read - lag(read) OVER (ORDER BY time))
END
) / extract(epoch from time - lag(time) OVER (ORDER BY time)) AS read_rate,
extract(epoch from time - lag(time) OVER (ORDER BY time)) AS "time lag"
FROM disk
WHERE time BETWEEN '2021-04-10 21:00:00'::timestamp AND '2021-04-10 21:01:00'::timestamp
AND tag_id = 1
ORDER BY time;
time | read_rate | time lag
----------------------------------------------------------------------------------------------------------------------------------
2021-04-10 21:00:00+08 | |
2021-04-10 21:00:01+08 | -52 | 1
2021-04-10 21:00:02+08 | 62 | 1
2021-04-10 21:00:03+08 | -30 | 1
2021-04-10 21:00:04+08 | 36 | 1
2021-04-10 21:00:05+08 | -65 | 1
2021-04-10 21:00:06+08 | 11 | 1
2021-04-10 21:00:07+08 | 41 | 1
2021-04-10 21:00:08+08 | -25 | 1
2021-04-10 21:00:09+08 | -34 | 1
......
Точки изменений перечисляют записи, которые отличаются от предыдущих. Такой тип запроса подходит для выявления изменений в относительно стабильных наборах данных:
ymatrix=# SELECT time, read FROM (
SELECT time,
read,
read - lag(read) OVER (ORDER BY TIME) AS diff
FROM disk
WHERE time BETWEEN '2021-04-10 21:00:00'::timestamp AND '2021-04-10 21:01:00'::timestamp
AND tag_id = 1 ) ht
WHERE diff IS NULL OR diff != 0
ORDER BY time;
time | read
-----------------------------------------------
2021-04-10 21:00:00+08 | 81.07
2021-04-10 21:00:01+08 | 29.56
2021-04-10 21:00:02+08 | 91.49
2021-04-10 21:00:03+08 | 61.62
2021-04-10 21:00:04+08 | 97.86
2021-04-10 21:00:05+08 | 32.89
2021-04-10 21:00:06+08 | 43.81
2021-04-10 21:00:07+08 | 85.05
2021-04-10 21:00:08+08 | 59.8
2021-04-10 21:00:09+08 | 25.86
(10 rows)
Можно использовать следующую упорядоченную агрегатную функцию для расчета непрерывного процентиля.
percentile_cont(fractions) WITHIN GROUP (ORDER BY sort_expression)
Примеры и возвращаемые результаты приведены ниже.
ymatrix=# SELECT tag_id,
percentile_cont(0.2) WITHIN GROUP
(ORDER BY read) AS read,
percentile_cont(0.3) WITHIN GROUP (ORDER BY write) AS write
FROM disk
GROUP BY tag_id
ORDER BY tag_id;
tag_id | read | write
-------+------+-------
1 | 19.87 | 29.86
2 | 19.95 | 29.88
3 | 20.06 | 29.93
(3 rows)
Параметр обозначает процент, если передано значение 0.5, это эквивалентно вычислению медианы.
Общее табличное выражение (CTE) позволяет определить и создать временное представление, тем самым упрощая крупные запросы. Реализуется с помощью ключевого слова WITH. Перед использованием вы можете понять различия между CTE и оператором CREATE VIEW по следующей таблице.
| CTE | CREATE VIEW | |
|---|---|---|
| Выражение | не является независимым SQL-оператором, а частью оператора, то есть выражением | независимый SQL-оператор |
| Область применения | созданное временное представление используется только в рамках принадлежащего ему запроса | созданное представление может использоваться во всех запросах |
Следующее CTE вычисляет максимальное и минимальное значения средней скорости чтения среди всех устройств:
WITH avg_read (tag_id, avg_read) AS (
SELECT tag_id, AVG(read) AS read FROM disk GROUP BY tag_id
) SELECT MAX(avg_read), MIN(avg_read) FROM avg_read;
Компонент временных данных matrixts, предоставляемый YMatrix, также включает временные функции, часто используемые в сценариях работы с временными данными. Сначала создайте этот компонент:
stats=# CREATE EXTENSION matrixts;
Функция time_bucket вычисляет среднее значение за заданный временной период.
| Имя параметра | Описание | Тип данных | По умолчанию |
|---|---|---|---|
| period | Размер временного окна | int16;int32;int64;interval | Нет |
| timestamp | Столбец, который необходимо преобразовать | int16;int32;int64;date;timestamp;timestamptz | Нет |
Следующий SQL-запрос вычисляет среднюю скорость чтения и записи каждые 5 минут с 2021-04-10 21:00:00 до 22:00:00:
ymatrix=# SELECT time_bucket('5 minutes', time) AS five_min,
AVG(read) as read,
AVG(write) as write
FROM disk
WHERE time BETWEEN '2021-04-10 21:00:00'::timestamp AND '2021-04-10 22:00:00'::timestamp
AND tag_id = 1
GROUP BY five_min
ORDER BY five_min;
five_min | read | write
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2021-04-10 21:00:00+08 | 48.614599999999996 | 49.48656666666666
2021-04-10 21:05:00+08 | 50.73533333333335 | 49.992566666666654
2021-04-10 21:10:00+08 | 51.6102333333333 | 49.99359999999999
2021-04-10 21:15:00+08 | 49.29116666666669 | 53.89146666666666
2021-04-10 21:20:00+08 | 49.67863333333332 | 50.47406666666665
2021-04-10 21:25:00+08 | 51.09013333333332 | 47.766733333333335
2021-04-10 21:30:00+08 | 49.55949999999999 | 50.440766666666654
2021-04-10 21:35:00+08 | 48.86253333333333 | 50.57290000000001
2021-04-10 21:40:00+08 | 51.061299999999974 | 47.028766666666684
2021-04-10 21:45:00+08 | 52.10353333333333 | 49.861466666666665
2021-04-10 21:50:00+08 | 51.780566666666694 | 51.4159
2021-04-10 21:55:00+08 | 51.83549999999998 | 49.124366666666674
2021-04-10 22:00:00+08 | 93.96 | 91.07
(13 rows)
Когда в течение периода времени отсутствуют данные и требуется их очистка, можно использовать функцию time_bucket_gapfill для заполнения пропущенных данных, чтобы данные равномерно распределились по указанному временному интервалу, что облегчает анализ. Существуют две стратегии заполнения: locf (last observation carried forward) и interpolate (интерполяция).
Предположим, что данные в примере таблицы disk следующие (применяется только к разделу 4.2):
ymatrix=# SELECT * FROM disk ORDER BY tag_id;
time | tag_id | read | write
------------------------+---------------------------------------------------------------------------------------------------------
2021-04-10 21:00:00+08 | 1 | 3.4 | 4.6
2021-04-10 21:50:00+08 | 1 | 4 | 2.7
2021-04-10 21:40:00+08 | 1 | 8.4 | 12
2021-04-10 21:20:00+08 | 1 | 2.9 | 6
2021-04-10 21:30:00+08 | 1 | 9 | 10.2
2021-04-10 21:10:00+08 | 1 | 5.2 | 6.6
2021-04-10 22:00:00+08 | 1 | 10 | 7
(7 rows)
Используйте функцию time_bucket_gapfill для заполнения данных каждые 5 минут с 21:00:00 до 22:00:00, чтобы получить результат.
SELECT time_bucket_gapfill('5 minutes', time) AS five_min,
locf(AVG(read)) as locf_read,
interpolate(AVG(read)) as interpolate_read
FROM disk
WHERE time BETWEEN '2021-04-10 21:00:00'::timestamp AND '2021-04-10 22:00:00'::timestamp
AND tag_id = 1
GROUP BY five_min
ORDER BY five_min;
five_min | locf_read | interpolate_read
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2021-04-10 21:00:00+08 | 3.4 | 3.4
2021-04-10 21:05:00+08 | 3.4 | 4.3
2021-04-10 21:10:00+08 | 5.2 | 5.2
2021-04-10 21:15:00+08 | 5.2 | 4.05
2021-04-10 21:20:00+08 | 2.9 | 2.9
2021-04-10 21:25:00+08 | 2.9 | 5.95
2021-04-10 21:30:00+08 | 9 | 9
2021-04-10 21:35:00+08 | 9 | 8.7
2021-04-10 21:40:00+08 | 8.4 | 8.4
2021-04-10 21:45:00+08 | 8.4 | 6.2
2021-04-10 21:50:00+08 | 4 | 4
2021-04-10 21:55:00+08 | 4 | 7
2021-04-10 22:00:00+08 | 10 | 10
(13 rows)
first возвращает самое раннее значение по времени:
ymatrix=# SELECT tag_id,
first(read, time) AS read,
first(write, time) AS write
FROM disk
GROUP BY tag_id
ORDER BY tag_id;
tag_id | read | write
-------+------+-------
1 | 11.51 | 86.61
2 | 50.07 | 25.9
3 | 83.72 | 10.5
(3 rows)
last возвращает самое последнее значение:
ymatrix=# SELECT tag_id,
last(read, time) AS read,
last(write, time) AS write
FROM disk
GROUP BY tag_id
ORDER BY tag_id;
tag_id | read | write
-------+------+-------
1 | 5.32 | 4.96
2 | 5.73 | 34.73
3 | 49.03 | 86.02
(3 rows)
last_not_null_value эквивалентна функции last с фильтрацией not null, возвращая последнее непустое значение:
ymatrix=# SELECT last_not_null_value(read, time)
FROM disk WHERE tag_id = 1;
last_not_null_value
----------------------------------------------------------------------------------------------------------------------------------
3.1
(1 row)
По сравнению с last_not_null_value, функция last_not_null возвращает не только значение, но и время. Возвращаемый тип — строка, формат: '["value", "time"]':
ymatrix=# SELECT last_not_null(read, time)
FROM disk WHERE tag_id = 1;
last_not_null
----------------------------------------------------------------------------------------------------------------------------------
["3.1","2021-11-05 17:32:51.754457+08"]
(1 row)