Устаревшие функции: neighbor, runningDifference, runningAccumulate
Три функции ClickHouse — neighbor(), runningDifference() и runningAccumulate() — были объявлены устаревшими (deprecated) начиная с версии 24.8. В ClickHouse 26.3 LTS их вызов без специального разрешения приводит к ошибке. Этот урок объясняет, почему функции были deprecated, как включить их для чтения legacy-кода и — главное — какие оконные функции использовать вместо них.
Почему эти функции deprecated
Три ключевые причины deprecation:
-
Зависимость от block-порядка. Эти функции оперируют строками внутри одного block (до 65 536 строк). Порядок строк в block определяется физическим расположением данных, а не ORDER BY запроса. При параллельном чтении (
max_threads > 1) разные потоки обрабатывают разные block, и результат становится недетерминированным. -
Сброс на границе block.
runningDifference()вычисляет разницу с предыдущей строкой, но первая строка каждого block даёт разницу с нулём.runningAccumulate()сбрасывает аккумулятор на каждой границе block. Это означает, что результат зависит от размера block (max_block_size), который может меняться. -
Невозможность гарантировать корректность при параллельной обработке. Оконные функции решают все эти проблемы:
OVER (ORDER BY ...)гарантирует детерминированный порядок, а вычисление происходит по всему окну, без сброса на block-границах.
Включение deprecated-функций
Для чтения и отладки legacy-кода:
SET allow_deprecated_functions = 1;
-- Теперь deprecated-функции доступны в текущей сессии
SELECT neighbor(value, -1) FROM t;
Не используйте deprecated-функции в новом коде. SET allow_deprecated_functions = 1 — только для анализа legacy-запросов и миграции. Во всех новых запросах используйте оконные функции.
neighbor() и замена на LAG/LEAD
neighbor(column, offset) возвращает значение из строки со смещением offset относительно текущей. Положительный offset — следующая строка, отрицательный — предыдущая.
-- DEPRECATED: neighbor
SET allow_deprecated_functions = 1;
SELECT
ts,
value,
neighbor(value, -1) AS prev_value -- предыдущая строка
FROM metrics
ORDER BY ts;
Замена — LAG/LEAD:
-- СОВРЕМЕННЫЙ ПОДХОД: LAG/LEAD
SELECT
ts,
value,
LAG(value, 1) OVER (ORDER BY ts) AS prev_value, -- предыдущая
LEAD(value, 1) OVER (ORDER BY ts) AS next_value -- следующая
FROM metrics;
| Аспект | neighbor() | LAG/LEAD |
|---|---|---|
| Порядок строк | Физический (block) | Логический (ORDER BY) |
| Параллелизм | Недетерминирован | Детерминирован |
| Дефолтное значение | 0/пустая строка | NULL (или 3-й аргумент) |
| PARTITION BY | Нет | Да |
runningDifference() и замена на LAG
runningDifference(column) вычисляет разницу текущего значения с предыдущим. Первая строка в block всегда даёт разницу с нулём.
-- DEPRECATED: runningDifference
SET allow_deprecated_functions = 1;
SELECT
ts,
value,
runningDifference(value) AS diff -- value - prev_value
FROM metrics
ORDER BY ts;
Проблема block-границ:
Block 1: [100, 200, 300] -> diff: [100, 100, 100] -- первое 100 = 100 - 0
Block 2: [400, 500] -> diff: [400, 100] -- 400 = 400 - 0 (сброс!)
-- Ожидалось: 400 - 300 = 100, но block не видит предыдущий block
Замена — LAG с вычитанием:
-- СОВРЕМЕННЫЙ ПОДХОД: LAG
SELECT
ts,
value,
value - LAG(value, 1) OVER (ORDER BY ts) AS diff
FROM metrics;
-- Первая строка: NULL (нет предыдущей), а не 0
-- Нет проблемы block-границ
runningAccumulate() и замена на SUM() OVER
runningAccumulate(state_column) выполняет нарастающую агрегацию, но сбрасывается на каждой границе block.
-- DEPRECATED: runningAccumulate
SET allow_deprecated_functions = 1;
SELECT
ts,
runningAccumulate(sumState(value)) AS running_sum
FROM metrics
ORDER BY ts;
Замена — агрегатные функции с OVER:
-- СОВРЕМЕННЫЙ ПОДХОД: SUM() OVER
SELECT
ts,
value,
SUM(value) OVER (ORDER BY ts ROWS UNBOUNDED PRECEDING) AS running_sum
FROM metrics;
-- С партиционированием по метрике:
SELECT
metric_name,
ts,
value,
SUM(value) OVER (
PARTITION BY metric_name
ORDER BY ts
ROWS UNBOUNDED PRECEDING
) AS metric_running_sum
FROM metrics;
Оконный вариант корректен при любом max_threads и max_block_size.
Полный пример миграции
Типичный legacy-запрос с тремя deprecated-функциями:
-- LEGACY (deprecated, недетерминированный результат):
SET allow_deprecated_functions = 1;
SELECT
ts,
value,
neighbor(value, -1) AS prev_value,
runningDifference(ts) AS time_gap,
runningAccumulate(sumState(value)) AS cumulative
FROM metrics
ORDER BY ts;
Модернизированный вариант:
-- MODERN (детерминированный, корректный при параллельном чтении):
SELECT
ts,
value,
LAG(value, 1) OVER (ORDER BY ts) AS prev_value,
ts - LAG(ts, 1) OVER (ORDER BY ts) AS time_gap,
SUM(value) OVER (ORDER BY ts ROWS UNBOUNDED PRECEDING) AS cumulative
FROM metrics;
Оконные функции подробно рассмотрены в уроке 04 этого модуля. Для полного понимания синтаксиса OVER, PARTITION BY, ROWS/RANGE — обратитесь к нему.
Ключевые выводы
- neighbor(), runningDifference(), runningAccumulate() deprecated с ClickHouse 24.8. В 26.3 LTS требуют
SET allow_deprecated_functions = 1. - Причина: зависимость от block-порядка, сброс на block-границах, недетерминированный результат при параллельном чтении.
- Замены:
neighbor()->LAG()/LEAD(),runningDifference()->col - LAG(col),runningAccumulate()->SUM()/AVG()/etc. OVER(). - Оконные функции гарантируют детерминированный результат через
ORDER BYв OVER-клаузе. Нет проблемы block-границ. - allow_deprecated_functions = 1 — только для анализа legacy-кода, не для нового кода.