Learning Platform
Глоссарий Troubleshooting
Урок 09.08 · 25 мин
Продвинутый
neighborrunningDifferencerunningAccumulateDeprecated FunctionsWindow FunctionsLAGLEADallow_deprecated_functions

Устаревшие функции: neighbor, runningDifference, runningAccumulate

Три функции ClickHouse — neighbor(), runningDifference() и runningAccumulate() — были объявлены устаревшими (deprecated) начиная с версии 24.8. В ClickHouse 26.3 LTS их вызов без специального разрешения приводит к ошибке. Этот урок объясняет, почему функции были deprecated, как включить их для чтения legacy-кода и — главное — какие оконные функции использовать вместо них.


Почему эти функции deprecated

Deprecated-функции и их замены на оконные функции
neighbor(col, offset)neighbor(column, offset) -- доступ к значению из соседней строки. Deprecated: результат зависит от физического порядка данных в block, а не от ORDER BY. При параллельном чтении порядок block непредсказуем.
runningDifference(col)runningDifference(column) -- разница текущего значения с предыдущим в block. Deprecated: первая строка каждого block даёт разницу с нулём (а не с предыдущим block). При параллельном чтении block-границы непредсказуемы.
runningAccumulate(state)runningAccumulate(state_col) -- нарастающая агрегация по block. Deprecated: аккумулятор сбрасывается на границе block. При параллельном чтении результат недетерминирован.
LAG / LEADLAG(col, offset, default) OVER (ORDER BY ...) -- детерминированный доступ к предыдущей строке. ORDER BY гарантирует порядок. PARTITION BY разделяет окна. Работает корректно при параллельном чтении.
col - LAG(col)col - LAG(col, 1) OVER (ORDER BY ...) -- разница с предыдущим значением. ORDER BY гарантирует корректный порядок. Нет проблемы block-границ: оконная функция видит всё окно целиком.
SUM() OVER()SUM(col) OVER (ORDER BY ... ROWS UNBOUNDED PRECEDING) -- нарастающая сумма. Работает по всему окну, не сбрасывается на block-границах. Поддерживает PARTITION BY для группировки.

Три ключевые причины deprecation:

  1. Зависимость от block-порядка. Эти функции оперируют строками внутри одного block (до 65 536 строк). Порядок строк в block определяется физическим расположением данных, а не ORDER BY запроса. При параллельном чтении (max_threads > 1) разные потоки обрабатывают разные block, и результат становится недетерминированным.

  2. Сброс на границе block. runningDifference() вычисляет разницу с предыдущей строкой, но первая строка каждого block даёт разницу с нулём. runningAccumulate() сбрасывает аккумулятор на каждой границе block. Это означает, что результат зависит от размера block (max_block_size), который может меняться.

  3. Невозможность гарантировать корректность при параллельной обработке. Оконные функции решают все эти проблемы: OVER (ORDER BY ...) гарантирует детерминированный порядок, а вычисление происходит по всему окну, без сброса на block-границах.


Включение deprecated-функций

Для чтения и отладки legacy-кода:

SET allow_deprecated_functions = 1;

-- Теперь deprecated-функции доступны в текущей сессии
SELECT neighbor(value, -1) FROM t;
WARNING

Не используйте 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;
TIP

Оконные функции подробно рассмотрены в уроке 04 этого модуля. Для полного понимания синтаксиса OVER, PARTITION BY, ROWS/RANGE — обратитесь к нему.


Ключевые выводы

  1. neighbor(), runningDifference(), runningAccumulate() deprecated с ClickHouse 24.8. В 26.3 LTS требуют SET allow_deprecated_functions = 1.
  2. Причина: зависимость от block-порядка, сброс на block-границах, недетерминированный результат при параллельном чтении.
  3. Замены: neighbor() -> LAG()/LEAD(), runningDifference() -> col - LAG(col), runningAccumulate() -> SUM()/AVG()/etc. OVER().
  4. Оконные функции гарантируют детерминированный результат через ORDER BY в OVER-клаузе. Нет проблемы block-границ.
  5. allow_deprecated_functions = 1 — только для анализа legacy-кода, не для нового кода.

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 3. Legacy-запрос вычисляет разницу timestamp между соседними событиями: SELECT ts, runningDifference(ts) AS gap FROM events ORDER BY ts. Какой современный эквивалент даёт детерминированный результат при параллельном чтении?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 10