Skip to content
ClickHouse Docs
ClickHouse DocsClickHouse Docs

Условные функции

Обзор

Прямое использование результатов условных выражений

Условные выражения всегда возвращают 0, 1 или NULL. Поэтому их результаты можно использовать напрямую, например так:

SELECT left < right AS is_small
FROM LEFT_RIGHT

Значения NULL в условных выражениях

Если в условных выражениях участвуют значения NULL, результатом также будет NULL.

SELECT
    NULL < 1,
    2 < NULL,
    NULL < NULL,
    NULL = NULL

Поэтому, если типы — Nullable, запросы следует составлять особенно внимательно.

Следующий пример это показывает: он завершается ошибкой, потому что в multiIf не добавлено условие с равенством.

SELECT
    left,
    right,
    multiIf(left < right, 'left is smaller', left > right, 'right is smaller', 'Both equal') AS faulty_result
FROM LEFT_RIGHT

Оператор CASE

Выражение CASE в ClickHouse предоставляет условную логику, аналогичную оператору CASE в SQL. Оно вычисляет условия и возвращает значения на основе первого совпадения.

ClickHouse поддерживает две формы CASE:

  1. CASE WHEN ... THEN ... ELSE ... END
    Эта форма обеспечивает максимальную гибкость и внутренне реализована с помощью функции multiIf. Каждое условие вычисляется независимо, а выражения могут включать неконстантные значения.
SELECT
    number,
    CASE
        WHEN number % 2 = 0 THEN number + 1
        WHEN number % 2 = 1 THEN number * 10
        ELSE number
    END AS result
FROM system.numbers
WHERE number < 5;

-- преобразуется в
SELECT
    number,
    multiIf((number % 2) = 0, number + 1, (number % 2) = 1, number * 10, number) AS result
FROM system.numbers
WHERE number < 5

  1. CASE <expr> WHEN <val1> THEN ... WHEN <val2> THEN ... ELSE ... END
    Эта более компактная форма оптимизирована для сопоставления с константными значениями и внутри использует caseWithExpression().

Например, следующий вариант является допустимым:

SELECT
    number,
    CASE number
        WHEN 0 THEN 100
        WHEN 1 THEN 200
        ELSE 0
    END AS result
FROM system.numbers
WHERE number < 3;

-- преобразуется в

SELECT
    number,
    caseWithExpression(number, 0, 100, 1, 200, 0) AS result
FROM system.numbers
WHERE number < 3

Эта форма также не требует, чтобы возвращаемые выражения были константами.

SELECT
    number,
    CASE number
        WHEN 0 THEN number + 1
        WHEN 1 THEN number * 10
        ELSE number
    END
FROM system.numbers
WHERE number < 3;

-- преобразуется в

SELECT
    number,
    caseWithExpression(number, 0, number + 1, 1, number * 10, number)
FROM system.numbers
WHERE number < 3

Ограничения

ClickHouse определяет тип результата выражения CASE (или его внутреннего эквивалента, например multiIf) до вычисления каких-либо условий. Это важно, когда возвращаемые выражения различаются по типу, например используют разные часовые пояса или числовые типы.

  • Тип результата выбирается на основе наибольшего совместимого типа среди всех ветвей.
  • После выбора этого типа все остальные ветви неявно приводятся к нему — даже если их логика никогда не будет выполнена во время исполнения.
  • Для таких типов, как DateTime64, где часовой пояс является частью сигнатуры типа, это может приводить к неожиданному поведению: часовой пояс, встретившийся первым, может использоваться для всех ветвей, даже если в других ветвях указаны другие часовые пояса.

Например, ниже все строки возвращают временную метку в часовом поясе первой совпавшей ветви, то есть Asia/Kolkata

SELECT
    number,
    CASE
        WHEN number = 0 THEN fromUnixTimestamp64Milli(0, 'Asia/Kolkata')
        WHEN number = 1 THEN fromUnixTimestamp64Milli(0, 'America/Los_Angeles')
        ELSE fromUnixTimestamp64Milli(0, 'UTC')
    END AS tz
FROM system.numbers
WHERE number < 3;

-- преобразуется в

SELECT
    number,
    multiIf(number = 0, fromUnixTimestamp64Milli(0, 'Asia/Kolkata'), number = 1, fromUnixTimestamp64Milli(0, 'America/Los_Angeles'), fromUnixTimestamp64Milli(0, 'UTC')) AS tz
FROM system.numbers
WHERE number < 3

Здесь ClickHouse видит несколько типов возвращаемого значения DateTime64(3, <timezone>). В качестве общего типа он выводит DateTime64(3, 'Asia/Kolkata' — первый встретившийся вариант, неявно приводя к нему другие ветви.

Это можно исправить, преобразовав значение в строку, чтобы сохранить нужное форматирование часового пояса:

SELECT
    number,
    multiIf(
        number = 0, formatDateTime(fromUnixTimestamp64Milli(0), '%F %T', 'Asia/Kolkata'),
        number = 1, formatDateTime(fromUnixTimestamp64Milli(0), '%F %T', 'America/Los_Angeles'),
        formatDateTime(fromUnixTimestamp64Milli(0), '%F %T', 'UTC')
    ) AS tz
FROM system.numbers
WHERE number < 3;

-- преобразуется в

SELECT
    number,
    multiIf(number = 0, formatDateTime(fromUnixTimestamp64Milli(0), '%F %T', 'Asia/Kolkata'), number = 1, formatDateTime(fromUnixTimestamp64Milli(0), '%F %T', 'America/Los_Angeles'), formatDateTime(fromUnixTimestamp64Milli(0), '%F %T', 'UTC')) AS tz
FROM system.numbers
WHERE number < 3

clamp

Добавленный в: v24.5.0

Ограничивает значение указанными минимальной и максимальной границами.

Если значение меньше минимума, возвращает минимум. Если значение больше максимума, возвращает максимум. В противном случае возвращает само значение.

Все аргументы должны быть сравнимых типов. Тип результата — наибольший совместимый тип среди всех аргументов.

Синтаксис

clamp(value, min, max)

Аргументы

  • value — Значение, которое нужно привести к диапазону. - min — Нижняя граница. - max — Верхняя граница.

Возвращаемое значение

Возвращает значение, приведённое к диапазону [min, max].

Примеры

Базовое использование

SELECT clamp(5, 1, 10) AS result;
┌─result─┐
│      5 │
└────────┘

Значение ниже минимума

SELECT clamp(-3, 0, 7) AS result;
┌─result─┐
│      0 │
└────────┘

Значение превышает максимум

SELECT clamp(15, 0, 7) AS result;
┌─result─┐
│      7 │
└────────┘

greatest

Добавленный в: v1.1.0

Возвращает наибольшее значение среди аргументов. Аргументы NULL игнорируются.

  • Для массивов возвращает лексикографически наибольший массив.
  • Для типов DateTime тип результата повышается до наиболее широкого типа (например, DateTime64, если он используется вместе с DateTime32).

Синтаксис

greatest(x1[, x2, ...])

Аргументы

  • x1[, x2, ...] — Одно или несколько значений для сравнения. Все аргументы должны иметь сравнимые типы. Any

Возвращаемое значение

Возвращает наибольшее из аргументов, приведённое к наибольшему совместимому типу. Any

Примеры

Числовые типы

-- The type returned is a Float64 as the UInt8 must be promoted to 64 bit for the comparison.
SELECT greatest(1, 2, toUInt8(3), 3.) AS result, toTypeName(result) AS type;
┌─result─┬─type────┐
│      3 │ Float64 │
└────────┴─────────┘

Массивы

SELECT greatest(['hello'], ['there'], ['world']);
┌─greatest(['hello'], ['there'], ['world'])─┐
│ ['world']                                 │
└───────────────────────────────────────────┘

Тип DateTime

-- The type returned is a DateTime64 as the DateTime32 must be promoted to 64 bit for the comparison.
SELECT greatest(toDateTime32('2025-01-02 12:00:00'), toDateTime64('2025-01-01 12:00:00.000', 3));
┌─greatest(toDateTime32('2025-01-02 12:00:00'), toDateTime64('2025-01-01 12:00:00.000', 3))─┐
│                                                                   2025-01-02 12:00:00.000 │
└───────────────────────────────────────────────────────────────────────────────────────────┘

if

Добавленный в: v1.1.0

Выполняет условное ветвление.

  • Если условие cond вычисляется в ненулевое значение, функция возвращает результат выражения then.
  • Если cond вычисляется в ноль или NULL, возвращается результат выражения else.

Параметр short_circuit_function_evaluation определяет, используется ли укороченное вычисление.

Если он включен, выражение then вычисляется только для строк, где cond истинно, а выражение else — только там, где cond ложно.

Например, при укороченном вычислении исключение деления на ноль не генерируется при выполнении следующего запроса:

SELECT if(number = 0, 0, intDiv(42, number)) FROM numbers(10)

then и else должны быть одного или близкого типа.

Синтаксис

if(cond, then, else)

Аргументы

  • cond — Проверяемое условие. UInt8 или Nullable(UInt8) или NULL
  • then — Выражение, возвращаемое, если cond равно true. - else — Выражение, возвращаемое, если cond равно false или NULL.

Возвращаемое значение

Результат одного из выражений then или else в зависимости от условия cond.

Примеры

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

SELECT if(1, 2 + 2, 2 + 6) AS res;
┌─res─┐
│   4 │
└─────┘

least

Добавленный в: v1.1.0

Возвращает наименьшее значение среди аргументов. Аргументы NULL игнорируются.

  • Для массивов возвращает лексикографически наименьший массив.
  • Для типов DateTime тип результата повышается до наибольшего типа (например, DateTime64, если он используется вместе с DateTime32).

Синтаксис

least(x1[, x2, ...])

Аргументы

  • x1[, x2, ...] — Одно или несколько значений для сравнения. Все аргументы должны быть сравнимых типов. Any

Возвращаемое значение

Возвращает наименьшее значение среди аргументов, приведённое к наибольшему совместимому типу. Any

Примеры

Числовые типы

-- The type returned is a Float64 as the UInt8 must be promoted to 64 bit for the comparison.
SELECT least(1, 2, toUInt8(3), 3.) AS result, toTypeName(result) AS type;
┌─result─┬─type────┐
│      1 │ Float64 │
└────────┴─────────┘

Массивы

SELECT least(['hello'], ['there'], ['world']);
┌─least(['hello'], ['there'], ['world'])─┐
│ ['hello']                              │
└────────────────────────────────────────┘

Типы DateTime

-- The type returned is a DateTime64 as the DateTime32 must be promoted to 64 bit for the comparison.
SELECT least(toDateTime32('2025-01-02 12:00:00'), toDateTime64('2025-01-01 12:00:00.000', 3));
┌─least(toDateTime32('2025-01-02 12:00:00'), toDateTime64('2025-01-01 12:00:00.000', 3))─┐
│                                                                2025-01-01 12:00:00.000 │
└────────────────────────────────────────────────────────────────────────────────────────┘

multiIf

Добавленный в: v1.1.0

Позволяет более компактно записывать оператор CASE в запросе. Вычисляет каждое условие по порядку. Для первого условия, которое истинно (ненулевое и не NULL), возвращает значение соответствующей ветви. Если ни одно из условий не истинно, возвращает значение else.

Параметр short_circuit_function_evaluation определяет, используется ли укороченное вычисление. Если оно включено, выражение then_i вычисляется только для строк, где ((NOT cond_1) AND ... AND (NOT cond_{i-1}) AND cond_i) истинно.

Например, при укороченном вычислении исключение деления на ноль не возникает при выполнении следующего запроса:

SELECT multiIf(number = 2, intDiv(1, number), number = 5) FROM numbers(10)

Все выражения в ветвях и else должны иметь общий супертип. Условия NULL считаются ложными.

Синтаксис

multiIf(cond_1, then_1, cond_2, then_2, ..., else)

Псевдонимы: caseWithoutExpression, caseWithoutExpr

Аргументы

  • cond_N — N-е вычисляемое условие, которое определяет, будет ли возвращено then_N. UInt8 или Nullable(UInt8) или NULL
  • then_N — Результат функции, если cond_N имеет значение true. - else — Результат функции, если ни одно из условий не имеет значение true.

Возвращаемое значение

Возвращает результат then_N для соответствующего cond_N, в противном случае возвращает значение else.

Примеры

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

CREATE TABLE LEFT_RIGHT (left Nullable(UInt8), right Nullable(UInt8)) ENGINE = Memory;
INSERT INTO LEFT_RIGHT VALUES (NULL, 4), (1, 3), (2, 2), (3, 1), (4, NULL);

SELECT
    left,
    right,
    multiIf(left < right, 'left is smaller', left > right, 'left is greater', left = right, 'Both equal', 'Null value') AS result
FROM LEFT_RIGHT;
┌─left─┬─right─┬─result──────────┐
│ ᴺᵁᴸᴸ │     4 │ Null value      │
│    1 │     3 │ left is smaller │
│    2 │     2 │ Both equal      │
│    3 │     1 │ left is greater │
│    4 │  ᴺᵁᴸᴸ │ Null value      │
└──────┴───────┴─────────────────┘
Navigation