概览
直接使用条件结果
条件表达式的结果始终是 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 语句
ClickHouse 中的 CASE 表达式提供了类似 SQL CASE 运算符的条件逻辑。它会对条件进行求值,并根据第一个匹配的条件返回相应的值。
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类型,结果类型会提升为最大的类型 (例如,与DateTime32混用时,结果为DateTime64) 。
语法
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
Introduced in: v1.1.0
执行条件分支。
- 如果条件
cond的求值结果为非零值,则函数返回表达式then的结果。 - 如果
cond的求值结果为零或 NULL,则返回else表达式的结果。
设置 short_circuit_function_evaluation 用于控制是否启用短路求值。
启用后,仅在 cond 为 true 的行上计算 then 表达式,而仅在 cond 为 false 的行上计算 else 表达式。
例如,启用短路求值后,执行以下查询时不会因除以零而抛出异常:
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,则返回该表达式。
返回值
根据条件 cond,返回 then 或 else 表达式的结果。
示例
用法示例
SELECT if(1, 2 + 2, 2 + 6) AS res;┌─res─┐
│ 4 │
└─────┘least
引入版本:v1.1.0
返回参数中的最小值。
NULL 参数会被忽略。
- 对于数组,返回按字典序最小的数组。
- 对于 DateTime 类型,结果类型会提升为最大的类型 (例如,与 DateTime32 混用时结果为 DateTime64) 。
语法
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 运算符。
按顺序计算各个条件。对于第一个为 true (非零且不为 NULL) 的条件,返回对应的分支值。
如果没有任何条件为 true,则返回 else 值。
设置 short_circuit_function_evaluation 用于控制
是否使用短路求值。如果启用,then_i 表达式仅会在
((NOT cond_1) AND ... AND (NOT cond_{i-1}) AND cond_i) 为 true 的行上计算。
例如,使用短路求值时,执行以下查询不会抛出除以零异常:
SELECT multiIf(number = 2, intDiv(1, number), number = 5) FROM numbers(10)所有分支表达式和 else 表达式都必须具有共同超类型。NULL 条件会被视为 false。
语法
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,函数返回的结果。
返回值
返回与匹配的 cond_N 对应的 then_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 │
└──────┴───────┴─────────────────┘