Перейти к содержимому
Последнее обновление

Аналитические функции

Кратко Аналитические функции Apache Doris (оконные функции) вычисляют значение для каждой строки результирующего набора, не сворачивая строки, как это делает GROUP BY. Apache Doris поддерживает стандартный набор функций: ROW_NUMBER, RANK, DENSE_RANK, NTILE, LAG, LEAD, FIRST_VALUE, LAST_VALUE, NTH_VALUE, PERCENT_RANK, CUME_DIST, а также все агрегатные функции (SUM, AVG, COUNT, MIN, MAX), используемые с OVER. Оптимизатор Nereids также преобразует WHERE row_num <= K в Top-N с учётом секций, расположенный ниже оконного оператора, поэтому запрос «10 лучших в каждой категории» не сортирует секцию целиком.

Аналитические функции Apache Doris: оконные функции SQL в Doris ранжируют строки, вычисляют накопительные итоги, получают предыдущие значения и рассчитывают средние по строкам; планировщик проталкивает фильтры Top-N в оконный оператор.

Зачем использовать аналитические функции в Apache Doris?​

Аналитические функции Apache Doris отвечают на три вопроса, которые возникают почти в любой аналитической нагрузке и на которые один лишь GROUP BY ответить не может.

  • «Пронумеровать все заказы каждого клиента в хронологическом порядке, затем оставить первые 10». Самосоединение по (customer_id, order_date) решает задачу, но требует дважды прочитать таблицу.
  • «Каким был накопительный итог продаж по дням и скользящее среднее за 7 дней?» GROUP BY сворачивает строки, которые вы хотите сохранить в результате.
  • «Как каждая строка соотносится с предыдущей?» Сравнения год к году, период к периоду, интервалы между событиями относятся к шаблону lag/lead. Без оконных функций для этого потребуется самосоединение с условием смещения, которое планировщик не может оптимизировать.

Аналитические функции Apache Doris решают все три задачи за один проход по данным, без самосоединений и с сохранением исходных строк.

Что такое аналитические функции Apache Doris?​

Аналитическая функция Apache Doris представляет собой функцию SQL, которая для каждой входной строки вычисляет значение по окну связанных строк, заданному предложением OVER. Окно может охватывать всю секцию, фиксированный диапазон вокруг текущей строки или любой промежуточный вариант. Число строк на выходе совпадает с числом строк на входе.

function(args) OVER ( [PARTITION BY ...] [ORDER BY ...] [<frame>] )

Основные термины

  • Предложение OVER: указывает Apache Doris, что этот вызов является оконной функцией, а не обычной агрегатной функцией. Обязательно.
  • PARTITION BY: разбивает входные данные на независимые группы. Вычисления выполняются отдельно для каждой секции. В отличие от партиций таблицы, эти секции формируются во время выполнения запроса.
  • ORDER BY (внутри OVER): упорядочивает строки в каждой секции. Необходимо для LAG, LEAD, ROW_NUMBER, RANK и любой рамки окна с PRECEDING/FOLLOWING.
  • Рамка окна: часть секции, которую функция читает для текущей строки. Apache Doris поддерживает ROWS BETWEEN ... PRECEDING/FOLLOWING/CURRENT ROW/UNBOUNDED ... и ограниченную форму RANGE.
  • PartitionTopN: внутренний оператор, который планировщик добавляет, когда может доказать, что фильтру WHERE rank <= K нужны только первые K строк каждой секции. Исходный код: CreatePartitionTopNFromWindow.java.

Как работают аналитические функции Apache Doris?​

Аналитические функции Apache Doris проходят пять этапов: планирование, перераспределение данных, сортировку, вычисление и проталкивание.

  1. Планирование. Оптимизатор разбирает предложение OVER в WindowExpression, нормализует рамку окна (CheckAndStandardizeWindowFunctionAndFrame) и объединяет вызовы оконных функций с одинаковыми PARTITION BY и ORDER BY в один физический оконный оператор. Вызовы с одинаковым секционированием и порядком строк не требуют дополнительной сортировки.
  2. Перераспределение по секциям. Если присутствует PARTITION BY, движок перераспределяет строки так, чтобы все строки одной секции попали на один бэкенд. Без PARTITION BY всё окно обрабатывается в одном конвейере, что задаёт верхнюю границу параллелизма.
  3. Сортировка внутри секции. Каждый бэкенд сортирует свои секции по столбцам ORDER BY. Если значения совпадают, порядок строк недетерминирован, если только ORDER BY не задаёт уникальный порядок. Поэтому документация предупреждает, что SUM() OVER (ORDER BY date_col) может возвращать разные результаты при совпадающих датах.
  4. Вычисление для каждой строки. Apache Doris проходит каждую секцию один раз, по ходу обновляя рамку окна. Функции ранжирования выдают по одному целому числу для каждой строки; агрегатные функции с ROWS UNBOUNDED PRECEDING поддерживают накопительный итог; агрегатные функции со скользящим окном добавляют новую строку и исключают строку, вышедшую за заднюю границу окна.
  5. Проталкивание фильтров и Top-N. CreatePartitionTopNFromWindow преобразует WHERE row_number() OVER (PARTITION BY a ORDER BY b) <= K в оператор PartitionTopN(K) ниже оконного оператора, поэтому из каждой секции в оконный оператор поступают только первые K строк. PushDownFilterThroughWindow переносит фильтры по столбцам PARTITION BY перед оконным оператором, поэтому Apache Doris фильтрует данные до сортировки, а не после неё.

Быстрый старт​

CREATE TABLE orders (
customer_id INT, order_date DATE, amount DECIMAL(10,2)
) DISTRIBUTED BY HASH(customer_id) BUCKETS 4
PROPERTIES ("replication_num" = "1");

INSERT INTO orders VALUES
(1,'2026-04-30',50),(1,'2026-05-01',80),(1,'2026-05-02',30),
(2,'2026-04-30',20),(2,'2026-05-01',90);

SELECT customer_id, order_date, amount,
ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY order_date) AS seq,
SUM(amount) OVER (PARTITION BY customer_id ORDER BY order_date) AS running_total,
LAG(amount,1,0) OVER (PARTITION BY customer_id ORDER BY order_date) AS prev_amount
FROM orders;

Ожидаемый результат

+-------------+------------+--------+-----+---------------+-------------+
| customer_id | order_date | amount | seq | running_total | prev_amount |
+-------------+------------+--------+-----+---------------+-------------+
| 1 | 2026-04-30 | 50 | 1 | 50 | 0 |
| 1 | 2026-05-01 | 80 | 2 | 130 | 50 |
| 1 | 2026-05-02 | 30 | 3 | 160 | 80 |
| 2 | 2026-04-30 | 20 | 1 | 20 | 0 |
| 2 | 2026-05-01 | 90 | 2 | 110 | 20 |
+-------------+------------+--------+-----+---------------+-------------+

Один проход, три столбца с результатами оконных функций, без самосоединений. Исходные пять строк сохранены. LAG(..., 1, 0) возвращает 0 вместо NULL для первой строки каждой секции.

Когда следует использовать аналитические функции Apache Doris?​

Аналитические функции Apache Doris подходят для любых вычислений, которые сохраняют строки и зависят от соседних строк, включая Top-N по группам, накопительные итоги, сравнения lag/lead, разбиение на группы для отчётов по перцентилям и расчёт доли от общего итога.

Подходящие задачи

  • Top-N по группам: «10 крупнейших заказов каждого клиента», «самый продаваемый товар в каждом регионе». Добавьте фильтр WHERE rn <= 10, и Apache Doris протолкнёт Top-N по секциям ниже оконного оператора.
  • Накопительные итоги, скользящие средние и центрированные скользящие средние с ROWS BETWEEN n PRECEDING AND m FOLLOWING.
  • Анализ изменений год к году, день ко дню и интервалов между событиями с помощью LAG и LEAD. Один проход по таблице заменяет самосоединение.
  • Разбиение на группы для отчётов по перцентилям или квартилям с помощью NTILE.
  • Отчётные запросы, которые сочетают значения отдельных строк с итогами по всей секции, например amount / SUM(amount) OVER (PARTITION BY region) AS share_of_region.

Неподходящие задачи

  • Обычная агрегация со сворачиванием строк. SELECT category, SUM(amount) FROM t GROUP BY category выполняет ту же работу без сортировки и без хранения каждой строки в памяти. Сначала используйте GROUP BY и переходите к OVER, только если вам также нужны неагрегированные столбцы.
  • Рамки RANGE с числовыми смещениями, например RANGE BETWEEN 5 PRECEDING AND CURRENT ROW. В Apache Doris рамка RANGE ограничена границами UNBOUNDED или CURRENT ROW; произвольное смещение RANGE n PRECEDING/FOLLOWING не поддерживается. Если нужно числовое смещение, используйте ROWS.
  • Окно без PARTITION BY. Весь результирующий набор попадает в один конвейер, который становится узким местом при большом объёме входных данных. Добавляйте ключ секционирования всякий раз, когда это позволяет задача.
  • ORDER BY по неуникальному столбцу, когда важен детерминированный результат. SUM(x) OVER (ORDER BY day) может возвращать разные накопительные итоги при повторных запусках, если несколько строк относятся к одному дню. Добавьте дополнительный критерий сортировки для строк с одинаковыми значениями; см. Обзор оконных функций.
  • Повторное вычисление одного и того же оконного результата при каждом обновлении. Если один и тот же запрос с ROW_NUMBER() выполняется каждую минуту по таблице с редко изменяющимися данными, асинхронное материализованное представление с частичным обновлением потребует меньше ресурсов, чем повторное выполнение оконных вычислений.

Дополнительные материалы​