Обзор
Прямое использование результатов условных выражений
Условные выражения всегда возвращают 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:
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
┌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)илиNULLthen— Выражение, возвращаемое, если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)илиNULLthen_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 │
└──────┴───────┴─────────────────┘