ClickHouse 函数极简教程
Posted 东海陈光剑
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ClickHouse 函数极简教程相关的知识,希望对你有一定的参考价值。
1. ClickHouse 函数
程序=数据结构+算法
——Nicklaus Wirth,图灵奖获得者,Pascal之父
“数据结构”是数据的存储组织形式,是数据元素之间的关系表达。有了这些“数据”之后,接下来就需要对这些数据进行计算处理——这就是“算法”。通常来说,特定的数据结构配备特定的算法。计算机领域的问题解决之道,就是设计合适的数据结构,加上合适的算法。
在前面的章节中,我们的主题是 ClickHouse 的基础数据类型和高级数据类型,同时,还介绍了这些数据类型的常用基础操作。本章介绍 ClickHouse 函数主题。
1.1. 概述
众所周知,数据结构,是相互之间存在关系的数据元素的集合,描述的是数据与数据之间的结构关系。数据元素之间关联,就会产生不同的结构,例如:数组、队列、树、图等结构。
1.1.1. ClickHouse函数简介
数据元素之间的“关系+操作”构成了数据类型,对已有的数据类型进行抽象就构成了抽象数据类型(ADT),就是封装了值和操作的模型。这里的“操作”,通常跟“功能”、“函数”、“方法”等词语背后表达的意义是同源的——都是表达了对数据的计算处理这个过程。
把数据处理过程中最普遍、最通用的功能模块抽象出来编写成“函数”,放到函数库中,提供数据处理计算服务——就是现代计算编程中最常见的 API —— 将一段经常需要使用的代码封装起来,在需要使用时可以直接调用——这就是计算机编程中的函数。
函数Y = f(X) 。其中,入参集合X被称为f的定义域,出参集合Y被称为f的值域。
入参X和返回值Y的数据类型,分别确定了该函数的定义域和对应域(对应域不是值域,函数的值域是函数的对应域的子集)。
定义域和对应域,加上函数名就组成了“函数签名”。
1.1.2. ClickHouse 函数分类
ClickHouse 中的函数有常规函数(regular functions)和聚合函数(aggregate function)两大类。常规函数的入参是每一行数据,对于每一行,函数的结果不依赖于其他行。聚合函数则是从不同的行,累积一组值(即它们依赖于整组行)进行聚合计算。另外,还有数组“炸裂”函数arrayJoin()、表函数等特殊功能的函数。
ClickHouse 中的函数可分为算术函数、数组操作函数、数组连接、位函数、位图函数、比较函数、条件函数、日期时间函数、编码函数、加密函数、扩展字典函数、文件操作函数、空值函数、哈希函数、IN 函数、自省函数(系统内部监控打点跟踪等功能)、IP地址函数、json函数、逻辑函数、机器学习函数、数学函数、NLP函数、随机函数、舍入函数、拆分合并函数、字符串函数、字符串替换函数、字符串搜索函数、时间窗口函数、元组函数、元组映射函数、类型转换函数、url函数、uuid函数、YM字典函数和其他函数等。
执行如下SQL:
SELECT *
FROM system.functions
ORDER BY name ASC
可以获取 ClickHouse 中的所有的函数 (常规函数+聚合函数)。
ClickHouse 函数清单见《附件1: ClickHouse 函数一览表》。
ClickHouse 有个内置的系统库system,我们可以去看一下系统库里面都有什么表。
USE system
SHOW TABLES
输出结果如下(从表名我们可以看出来这些表是做什么的):
┌─name───────────────────────────┐
│ aggregate_function_combinators │
......
│ databases │
......
│ functions │
......
└────────────────────────────────┘
66 rows in set. Elapsed: 0.014 sec.
1.1.3. 表级别函数
查看 ClickHouse 中有哪些表级别函数:
SELECT *
FROM table_functions
输出:
┌─name───────────────┐
│ dictionary │
│ numbers_mt │
│ view │
│ cosn │
│ generateRandom │
│ remote │
│ input │
│ s3Cluster │
│ values │
│ s3 │
│ url │
│ remoteSecure │
│ sqlite │
│ zeros │
│ jdbc │
│ zeros_mt │
│ postgresql │
│ odbc │
│ executable │
│ clusterAllReplicas │
│ cluster │
│ merge │
│ null │
│ file │
│ numbers │
└────────────────────┘
25 rows in set. Elapsed: 0.004 sec.
看一下 ClickHouse 都支持哪些表引擎:
SELECT name
FROM table_engines
输出:
┌─name───────────────────────────────────┐
│ PostgreSQL │
│ RabbitMQ │
│ Kafka │
│ S3 │
│ ExecutablePool │
│ MaterializedView │
│ MaterializedPostgreSQL │
│ EmbeddedRocksDB │
│ View │
│ JDBC │
│ Join │
│ ExternalDistributed │
│ Executable │
│ Set │
│ Dictionary │
│ GenerateRandom │
│ LiveView │
│ MergeTree │
│ Memory │
│ Buffer │
│ MongoDB │
│ URL │
│ ReplicatedVersionedCollapsingMergeTree │
│ ReplacingMergeTree │
│ ReplicatedSummingMergeTree │
│ COSN │
│ ReplicatedAggregatingMergeTree │
│ ReplicatedCollapsingMergeTree │
│ File │
│ ReplicatedGraphiteMergeTree │
│ ReplicatedMergeTree │
│ ReplicatedReplacingMergeTree │
│ VersionedCollapsingMergeTree │
│ SummingMergeTree │
│ Distributed │
│ TinyLog │
│ GraphiteMergeTree │
│ SQLite │
│ CollapsingMergeTree │
│ Merge │
│ AggregatingMergeTree │
│ ODBC │
│ Null │
│ StripeLog │
│ Log │
└────────────────────────────────────────┘
45 rows in set. Elapsed: 0.010 sec.
1.1.4. 聚合函数算子
SELECT *
FROM aggregate_function_combinators
输出:
┌─name────────┬─is_internal─┐
│ SimpleState │ 0 │
│ OrDefault │ 0 │
│ Distinct │ 0 │
│ Resample │ 0 │
│ ForEach │ 0 │
│ OrNull │ 0 │
│ Merge │ 0 │
│ State │ 0 │
│ Array │ 0 │
│ Null │ 1 │
│ Map │ 0 │
│ If │ 0 │
└─────────────┴─────────────┘
12 rows in set. Elapsed: 0.006 sec.
1.2. 算术运算函数
对于所有算术函数(arithmetic functions),会对计算结果根据根据位数、是否有符号以及是否浮动,最小值等条件进行类型推断,采用适合的最小数字类型。如果没有足够的位,则采用最高位类型。例如:
SELECT
toTypeName(0),
toTypeName(0 + 0),
toTypeName((0 + 0) + 0),
toTypeName(((0 + 0) + 0) + 0)
FORMAT Vertical
输出:
toTypeName(0): UInt8
toTypeName(plus(0, 0)): UInt16
toTypeName(plus(plus(0, 0), 0)): UInt32
toTypeName(plus(plus(plus(0, 0), 0), 0)): UInt64
算术函数适用于 UInt8、UInt16、UInt32、UInt64、Int8、Int16、Int32、Int64、Float32 或 Float64 中的任何类型对。溢出的产生方式与 C++ 中的相同。ClickHouse 中算术运算函数一览表如下。
函数 | 功能说明 | 使用实例 |
plus(a, b), a + b | 加法运算。还可以添加带有日期或日期和时间的整数。在日期的情况下,添加一个整数意味着添加相应的天数。对于带时间的日期,这意味着添加相应的秒数。 | 1.数字加法: SELECT 1 + 1┌─plus(1, 1)─┐│ 2 │└────────────┘ 2.日期、时间加法 SELECT toDate('2022-03-11') + 1 AS d, toDateTime('2022-03-11 18:42:14') + 10 AS t ┌──────────d─┬───────────────────t─┐ │ 2022-03-12 │ 2022-03-11 18:42:24 │ └────────────┴─────────────────────┘ |
minus(a, b), a - b | 减法运算。结果总是有符号类型。跟加法类似,减法运算也可以从日期或日期与时间计算整数 | 1.数字减法 SELECT 5 - 1 AS a, toTypeName(a) AS t ┌─a─┬─t─────┐ │ 4 │ Int16 │ └───┴───────┘ 2.日期、时间减法 SELECT toDate('2022-03-11') - 1 AS d, toDateTime('2022-03-11 18:42:14') - 10 AS t ┌──────────d─┬───────────────────t─┐ │ 2022-03-10 │ 2022-03-11 18:42:04 │ └────────────┴─────────────────────┘ |
multiply(a, b), a * b | 乘法运算。 | SELECT 8 * 8┌─multiply(8, 8)─┐│ 64 │└────────────────┘ |
divide(a, b), a / b | 除法运算。结果类型始终为浮点类型。它不是整数除法。对于整数除法,请使用“intDiv”函数。除以零时,您会得到“inf”、“-inf”或“nan”。 | SELECT 1 / 0 AS a, 0 / 0 AS b, 8 / 4 AS c, toTypeName(a), toTypeName(b), toTypeName(c) FORMAT Vertical Row 1: ────── a: inf b: nan c: 2 toTypeName(divide(1, 0)): Float64 toTypeName(divide(0, 0)): Float64 toTypeName(divide(8, 4)): Float64 |
intDiv(a, b) | 整数除法。结果按绝对值向下舍入。除以 0 会报错。 | 1.整数除法 SELECT intDiv(7, 3) AS a, 7 / 3 AS b┌─a─┬──────────────────b─┐│ 2 │ 2.3333333333333335 │└───┴────────────────────┘ 2.除以 0 报错 SELECT intDiv(1, 0) Received exception from server (version 21.12.1): Code: 153. DB::Exception: Received from localhost:9000. DB::Exception: Division by zero: While processing intDiv(1, 0). (ILLEGAL_DIVISION) |
intDivOrZero(a, b) | 与“intDiv”的不同之处在于它在除以0时返回0。 | SELECT intDivOrZero(1, 0) AS a, intDivOrZero(1, 2) AS b, intDivOrZero(5, 3) AS c┌─a─┬─b─┬─c─┐│ 0 │ 0 │ 1 │└───┴───┴───┘ |
modulo(a, b), a % b | 取余运算。b 如果是 0,报错。 | SELECT 7.7 % 3 AS a, 7 % 3 AS b, toTypeName(a), toTypeName(b) FORMAT Vertical Row 1: ────── a: 1.7000000000000002 b: 1 toTypeName(modulo(7.7, 3)): Float64 toTypeName(modulo(7, 3)): UInt8 |
moduloOrZero(a, b) | 取余运算。b 如果是 0,返回 0。 | SELECT moduloOrZero(1, 0) AS a, moduloOrZero(7, 3) AS b┌─a─┬─b─┐│ 0 │ 1 │└───┴───┘ |
negate(a), -a | 取负数。 | SELECT 1 AS a, -a┌─a─┬─negate(1)─┐│ 1 │ -1 │└───┴───────────┘ |
abs(a) | 取绝对值。 | SELECT -1 AS a, abs(a) AS b┌──a─┬─b─┐│ -1 │ 1 │└────┴───┘ |
gcd(a, b) | 求最大公约数。 | SELECT gcd(12, 16) AS a┌─a─┐│ 4 │└───┘ |
lcm(a, b) | 求最小公倍数。 | SELECT lcm(12, 16) AS a┌──a─┐│ 48 │└────┘ |
max2(v1,v2) | 求v1,v2 中大的数。 | SELECT max2(1, 2)┌─max2(1, 2)─┐│ 2 │└────────────┘ |
min2(v1,v2) | 求v1,v2 中小的数。 | SELECT min2(1, 2)┌─min2(1, 2)─┐│ 1 │└────────────┘ |
1.3. 数组操作函数
ClickHouse中常用的数组操作函数一览表如下。
函数 | 功能说明 | 使用实例 |
empty(x) | 检查输入数组是否为空。另外,empty()函数还可以判断字符串是否为空。 | SELECT empty([]) AS a, empty([1]) AS b, empty('') AS c┌─a─┬─b─┬─c─┐│ 1 │ 0 │ 1 │└───┴───┴───┘ |
notEmpty(x) | 与empty(x)逻辑相反。 | SELECT notEmpty([]) AS a, notEmpty([1]) AS b, notEmpty('') AS c┌─a─┬─b─┬─c─┐│ 0 │ 1 │ 0 │└───┴───┴───┘ |
length(x) | 返回数组元素个数(即数组长度)。结果类型为 UInt64。该函数也适用于字符串。 | SELECT '' AS a, [] AS b, [1] AS c, length(a) AS len_a, length(b) AS len_b, length(c) AS len_cFORMAT VerticalQuery id: 80d439a6-ce84-4541-bd6c-5eebf5c537a8Row 1:──────a: b: []c: [1]len_a: 0len_b: 0len_c: 1 |
range([start, ] end [, step]) | 构造数组。返回从 start 到 end - 1 的 UInt 数字数组,可以定制步长 step (默认为 1)。 | SELECT range(10) AS a, range(1, 10) AS b, range(1, 10, 2) AS cFORMAT VerticalQuery id: bbc61a37-648f-4e98-bf38-b2c41808bbf6Row 1:──────a: [0,1,2,3,4,5,6,7,8,9]b: [1,2,3,4,5,6,7,8,9]c: [1,3,5,7,9] |
array(x1, ...)等价算子:[x1, ...] | 从参数x1, ...创建一个数组。 | SELECT 1 AS a, 2 AS b, 3 AS c, [a, b, c], [a, b, c]FORMAT VerticalQuery id: 8028676b-50c5-4ee4-8720-9ccebb1f1ee7Row 1:──────a: 1b: 2c: 3array(1, 2, 3): [1,2,3]array(1, 2, 3): [1,2,3] |
arrayConcat(arrays) | 把多个数组拼接成一个数组。 | SELECT arrayConcat([1, 2], [3, 4], [5, 6]) AS resQuery id: 0e6b86af-8efb-43b4-b904-8bfb3f62786a┌─res───────────┐│ [1,2,3,4,5,6] │└───────────────┘1 rows in set. Elapsed: 0.001 sec. |
arrayElement(arr, n)等价算子:arr[n] | 获取数组 arr 中下标为 n 的元素。第 1 个元素的下标为 1。 | SELECT [1, 2, 3, 4, 5] AS arr, arr[1] AS a1, arr[2] AS a2FORMAT VerticalQuery id: d34fb174-49c9-41bd-993a-123cab8f4fc8Row 1:──────arr: [1,2,3,4,5]a1: 1a2: 2 |
has(arr, x) | 判断数组 arr 是否包含元素 x。如果包含,返回 1;不包含返回 0。 | SELECT [1, 2, 3, 4, 5] AS arr, 3 AS x, has(arr, x) AS resFORMAT VerticalQuery id: 6226fbba-fcba-449d-bb30-d281dd29f8a6Row 1:──────arr: [1,2,3,4,5]x: 3res: 1 |
hasAll(set, subset) | 判断subset 是否是 set 的子数组。如果是,返回 1;不是,则返回 0。 | SELECT [1, 2, 3, 4, 5] AS set, [1, 3] AS subset, hasAll(set, subset) AS resFORMAT VerticalQuery id: d550e8d7-08f3-429d-aa62-44044b90d3efRow 1:──────set: [1,2,3,4,5]subset: [1,3]res: 1 |
hasAny(array1, array2) | 检查两个数组是否有任意元素的交集。有交集返回 1,无交集返回 0。 | SELECT [1, 2, 3, 4, 5] AS set, [1, 3] AS subset, [8, 9] AS y, hasAny(set, subset) AS res1, hasAny(set, y) AS res2FORMAT VerticalQuery id: 5a719a60-a247-479c-9876-044e4246a5beRow 1:──────set: [1,2,3,4,5]subset: [1,3]y: [8,9]res1: 1res2: 0 |
indexOf(arr, x) | 返回第一个“x”元素在数组 arr 中的索引下标(从 1 开始),如果不存在,则返回 0。 | SELECT [1, 2, 3, 4, 5] AS arr, 3 AS x, indexOf(arr, x) AS resFORMAT VerticalQuery id: cca82f39-ee51-4cd8-92e0-8ca792def342Row 1:──────arr: [1,2,3,4,5]x: 3res: 3 |
arraySlice(array, offset[, length]) | 返回数组的一个切片。- array - 目标数组。- offset -开始偏移量。正值表示左侧偏移,负值表示右侧。数组下标从 1 开始。- length - 所需切片的长度。 | SELECT arraySlice([1, 2, 3, 4, 5], 2, 3) AS resQuery id: 8f50e22b-7fe0-46a5-8fb0-8102db0de1b3┌─res─────┐│ [2,3,4] │└─────────┘ |
arraySort(arr) | 数组排序,默认升序(ascending order)。 | SELECT arraySort([1, 3, 2, 5, 7, 6, 0])Query id: ff99177d-f59c-4163-84b1-2eaa82a26b5f┌─arraySort([1, 3, 2, 5, 7, 6, 0])─┐│ [0,1,2,3,5,6,7] │└──────────────────────────────────┘ |
arraySort(func, arr) | 高阶函数。根据 func 函数(lambda)规则对数组 arr 进行排序。 | SELECT arraySort(x -> (-x), [1, 2, 3]) AS resQuery id: a12359ca-6a2c-4efc-a606-7cd415c8e985┌─res─────┐│ [3,2,1] │└─────────┘ |
arrayReverseSort(arr) | 数组降序排序(descending order)。 | SELECT arrayReverseSort([1, 3, 3, 5, 7, 0])Query id: eaf0b594-29a9-481e-8bea-4e408dc31333┌─arrayReverseSort([1, 3, 3, 5, 7, 0])─┐│ [7,5,3,3,1,0] │└──────────────────────────────────────┘ |
arrayReverseSort(func,arr) | 根据 func函数规则,对数组arr逆序排序(descending order)。 | SELECT arrayReverseSort(x -> (-x), [1, 2, 3]) AS resQuery id: b992c3dd-25d7-4d46-a71c-ac467035128f┌─res─────┐│ [1,2,3] │└─────────┘ |
arrayUniq(arr) | 计算数组 arr 中不重复元素个数。 | SELECT arrayUniq([1, 2, 2, 3, 4, 5, 5, 6, 6, 6]) AS resQuery id: c3b5ea50-38b0-4b2a-ae22-4518985cf46a┌─res─┐│ 6 │└─────┘ |
arrayDistinct(array) | 数组去重。 | SELECT arrayDistinct([1, 2, 2, 3, 4, 5, 5, 6, 6, 6]) AS resQuery id: 3588ebd9-d408-4010-8c99-84054660bb58┌─res───────────┐│ [1,2,3,4,5,6] │└───────────────┘ |
arrayIntersect(arr1,arr2,...) | 计算数组交集。入参可为多个数组,返回一个包含所有源数组中存在的元素的数组。 | SELECT arrayIntersect([1, 2], [3, 4], [2, 3]) AS no_intersect, arrayIntersect([1, 2, 5], [1, 2, 3], [1, 2, 4]) AS intersectQuery id: 08d3bec1-248f-4006-b620-f9e70765e60e┌─no_intersect─┬─intersect─┐│ [] │ [2,1] │└──────────────┴───────────┘ |
arrayReduce(agg_func, arr1, arr2, ..., arrN) | 将聚合函数应用于数组元素并返回其结果。聚合函数的名称以单引号 'max'、'sum' 中的字符串形式传递。使用参数聚合函数时,参数在括号中的函数名称后指示 'uniqUpTo(6)'。 | SELECT arrayReduce('uniq', [1, 2, 3, 4, 5, 5, 5, 6, 6])Query id: 5072b860-c5ed-4328-8447-323253a35597┌─arrayReduce('uniq', [1, 2, 3, 4, 5, 5, 5, 6, 6])─┐│ 6 │└──────────────────────────────────────────────────┘ |
arrayReverse(arr) | 数组逆序。 | SELECT arrayReverse([1, 2, 3])Query id: e37b8751-e81e-4a94-8444-5764935e5240┌─arrayReverse([1, 2, 3])─┐│ [3,2,1] │└─────────────────────────┘ |
flatten(arr1,arr2,...) | 数组拍平。 | SELECT flatten([[[1, 2, 3]], [[2, 3, 4], [3, 4, 5]]]) AS resQuery id: 6fde8afe-b038-43e6-b6f7-fe107f3a096d┌─res─────────────────┐│ [1,2,3,2,3,4,3,4,5] │└─────────────────────┘ |
arrayZip(arr1, arr2, ..., arrN) | 将多个数组,合成一个元组数组。 | SELECT arrayZip(['a', 'b', 'c'], [3, 2, 1])Query id: b4685efd-9179-42cc-89f8-6179cb0daa61┌─arrayZip(['a', 'b', 'c'], [3, 2, 1])─┐│ [('a',3),('b',2),('c',1)] │└──────────────────────────────────────┘ |
arrayMap(func, arr, ...) | 将 arr 数组中的每个元素应用 func 函数映射规则,返回新的数组。 | SELECT arrayMap(x -> (x * x), [1, 2, 3]) AS resQuery id: d041bf8f-05f9-4c48-9a99-6fbbfeb509be┌─res─────┐│ [1,4,9] │└─────────┘SELECT arrayMap((x, y) -> (x * x, y * y), [1, 2, 3], [4, 5, 6]) AS resQuery id: 1553c910-8ffa-4162-af60-1ccc5e47da82┌─res────────────────────┐│ [(1,16),(4,25),(9,36)] │└────────────────────────┘ |
arrayFilter(func, arr1, …) | 根据函数 func 规则过滤数组中的元素。 | SELECT [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] AS arr, arrayFilter(x -> ((x % 2) = 0), arr) AS resFORMAT VerticalQuery id: d8c6564d-1845-4403-bff5-e1cb5b19ad04Row 1:──────arr: [1,2,3,4,5,6,7,8,9,10]res: [2,4,6,8,10] |
arrayMin([func,] arr) | 计算数组最小元素。如果指定 func,则按照 func 规则映射之后计算最小值。 | SELECT arrayMin([1, 2, 4]) AS res;┌─res─┐│ 1 │└─────┘SELECT arrayMin(x -> (-x), [1, 2, 4]) AS res;┌─res─┐│ -4 │└─────┘ |
arrayMax([func,] arr) | 计算数组最大元素。如果指定 func,则按照 func 规则映射之后计算最大值。 | SELECT arrayMax([1, 2, 4]) AS res;┌─res─┐│ 4 │└─────┘SELECT arrayMax(x -> (-x), [1, 2, 4]) AS res;┌─res─┐│ -1 │└─────┘ |
arraySum([func,] arr) | 返回源数组中元素的总和。如果指定了 func 函数,则返回此函数转换的元素的总和。 | SELECT arraySum([2, 3]) AS res;┌─res─┐│ 5 │└─────┘SELECT arraySum(x -> (x * x), [1, 2, 3]) AS res┌─res─┐│ 14 │└─────┘ |
arrayAvg([func,] arr) | 计算平均值。如果指定了函数 func,则按照 func 映射规则之后的元素值计算。 | SELECT arrayAvg([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) AS resQuery id: d39dcead-1136-40a8-9854-ece4c7970e44┌─res─┐│ 5.5 │└─────┘SELECT arrayAvg(x -> (x * x), [2, 4]) AS resQuery id: fb25c538-270c-46c6-b6b6-312c87887238┌─res─┐│ 10 │└─────┘ |
arrayProduct(arr) | 元素相乘。 | SELECT arrayProduct([1, 2, 3, 4, 5, 6]) AS resQuery id: 0070f0f8-92b0-4e70-a7a6-85eb5db36e2f┌─res─┐│ 720 │└─────┘ |
arrayJoin(arr) | 数组“炸开”,列转行,类似 Hive 中的 explode() 函数。 | SELECT arrayJoin([1, 2, 3]) AS e, 'abc' AS a Query id: bd63929f-3809-45a2-a7c9-7e98db6c20c7 ┌─e─┬─a───┐ │ 1 │ abc │ │ 2 │ abc │ │ 3 │ abc │ └───┴─────┘ |
1.4. 字符串操作函数
字符串可以说是 SQL 中最常用的数据类型了。只要人能阅读能看到的数据,都可以用字符串来表示。ClickHouse 中提供了丰富的字符串处理函数,清单如下。
函数 | 功能说明 | 使用实例 |
empty(x) | 字符串判空。空字符串返回“1”;非空字符串返回“0”。 | SELECT empty('') AS res1, empty('a') AS res2Query id: 15a1071d-54c5-44b2-8af1-e2cb3eacfb94┌─res1─┬─res2─┐│ 1 │ 0 │└──────┴──────┘ |
notEmpty(x) | 与上面的empty(x)逻辑相反。字符串非空判断。非空字符串返回“1”;空字符串返回“0”。 | SELECT notEmpty('') AS res1, notEmpty('a') AS res2Query id: ed0c29bb-d268-42f2-a6a8-7a4f42aa4c27┌─res1─┬─res2─┐│ 0 │ 1 │└──────┴──────┘ |
length(x) | 返回以字节为单位的字符串长度(不是字符个数)。结果类型为 UInt64。该函数也适用于数组。NULL 的长度是 NULL。 | SELECT length('') AS res1, length('abc') AS res2, length(NULL) AS res3Query id: bd54cfc9-0059-4b30-98f1-182d24bb08f1┌─res1─┬─res2─┬─res3─┐│ 0 │ 3 │ ᴺᵁᴸᴸ │└──────┴──────┴──────┘ |
leftPad('x', length[, 'pad_string']) | 左补齐字符串。参数说明:x - 需要填充的字符串。String 类型。length - 结果字符串的长度。如果值小于输入字符串长度,则输入字符串按原样返回。UInt类型。pad_string - 填充输入字符串的字符串。可选。如果未指定,则输入字符串将填充空格。 | SELECT leftPad('10', 7, '0')Query id: 5fa441cb-6c3c-473e-bbe9-d92b31610c8b┌─leftPad('10', 7, '0')─┐│ 0000010 │└───────────────────────┘ |
rightPadPad('x', length[, 'pad_string']) | 类似地,有右补齐字符串。 | SELECT rightPad('abc', 11, '*')Query id: 44982f4e-e0f7-412a-9074-459579547b06┌─rightPad('abc', 11, '*')─┐│ abc******** │└──────────────────────────┘ |
lower(x) | 转小写。 | SELECT 'aBcD' AS x, lower(x) AS resQuery id: 700f6c28-881b-46ca-9fe0-d3e5bb836dc4┌─x────┬─res──┐│ aBcD │ abcd │└──────┴──────┘ |
upper(x) | 转大写。 | SELECT 'aBcD' AS x, lower(x) AS lx, upper(x) AS uxQuery id: 37cda845-f767-44fd-83ea-ee9b726236c0┌─x────┬─lx───┬─ux───┐│ aBcD │ abcd │ ABCD │└──────┴──────┴──────┘ |
repeat(s, n) | 重复 n 次字符串 s。 | SELECT repeat('abc', 3)Query id: a394b92b-41d5-48a4-bb67-a0f253be29df┌─repeat('abc', 3)─┐│ abcabcabc │└──────────────────┘ |
concat(s1, s2, ...) | 拼接字符串。 | SELECT concat('Hello', ', ', 'World!') AS resQuery id: cce740c7-00a1-4535-ba74-bc8f5153652b┌─res───────────┐│ Hello, World! │└───────────────┘ |
substring(s, offset, length) | 返回子串。offset 从 1 开始。 | SELECT 'abcdefg' AS s, substring(s, 1, 3) AS resQuery id: d2759ab9-24b9-4e4b-99cb-bfdd0471054a┌─s───────┬─res─┐│ abcdefg │ abc │└─────────┴─────┘ |
base64Encode(s) | 将字符串 s 编码为 base64 | SELECT base64Encode('1234567890')Query id: 1203b202-51d7-40ff-a42f-3b7ed5b0fbc4┌─base64Encode('1234567890')─┐│ MTIzNDU2Nzg5MA== │└────────────────────────────┘ |
base64Decode(x) | 从base64 编码字符串解码原始字符串。 | SELECT base64Decode('MTIzNDU2Nzg5MA==') AS resQuery id: 77d82caa-45a3-4f10-9205-e690cd278e78┌─res────────┐│ 1234567890 │└────────────┘ |
startsWith(s, prefix) | 判断字符串 s 是否以 prefix 开头。 | SELECT startsWith('abc', 'a') AS resQuery id: dbd3f560-3857-4422-8cb4-da4c16475669┌─res─┐│ 1 │└─────┘ |
endsWith(s, suffix) | 判断字符串 s 是否以 suffix 结尾。 | SELECT endsWith('abc', 'c') AS resQuery id: 0f6fd140-8d3b-4903-8636-6beee5bc8b0e┌─res─┐│ 1 │└─────┘ |
trimBoth(input_string) | 从字符串的两端删除所有连续出现的公共空白(ASCII 字符中的 32)。它不会删除其他类型的空白字符(制表符、不间断空格等)。 | SELECT trimBoth(' a bc ')Query id: 86861168-89d1-4b12-bdeb-c5640d4a33dc┌─trimBoth(' a bc ')─┐│ a bc │└─────────────────────────┘ |
extractTextFromhtml(x) | 从 HTML 中提取纯文本。 | SELECT extractTextFromHTML(' <p> A text <i>with</i><b>tags</b>. <!-- comments --> </p> ') AS resQuery id: 4a2096e9-3e16-40c3-945d-d02a173cbf11┌─res────────────────┐│ A text with tags . │└────────────────────┘SELECT extractTextFromHTML(html) AS resFROM url('https://www.baidu.com/', RawBLOB, 'html String')FORMAT VerticalQuery id: 3a85242e-007f-4608-9f4c-82e71458eb2bRow 1:──────res: 百度一下,你就知道 新闻 hao123 地图 直播 视频 贴吧 学术 更多 ...... |
replaceOne(haystack, pattern, replacement) | 字符串替换。只替换找到的满足匹配目标字符第一个匹配的字符。 | SELECT replaceOne('abc**defg**', '*', '$') AS resQuery id: 5fa4c73e-1912-4b8a-a3ab-2cab2ea749a1┌─res─────────┐│ abc$*defg** │└─────────────┘ |
replaceAll(haystack, pattern, replacement) | 字符串替换。全部替换满足匹配目标字符模式的字符。 | SELECT replaceAll('abc**defg**', '*', '$') AS resQuery id: 86350017-f1b5-4575-80f3-588608a61360┌─res─────────┐│ abc$$defg$$ │└─────────────┘ |
replaceRegexpOne(haystack, pattern, replacement) | 字符串替换。只替换找到的第一个满足正则匹配模式的字符。 | SELECT replaceRegexpOne('abc123def09', '(\\\\d)', '$') AS resQuery id: 8269034b-dfc1-41de-a2c3-74bc9b629b99┌─res─────────┐│ abc$23def09 │└─────────────┘ |
replaceRegexpAll(haystack, pattern, replacement) | 字符串替换。全部替换满足正则匹配模式的字符。 | SELECT replaceRegexpAll('abc123def09', '(\\\\d)', '$') AS resQuery id: c6a71725-c81c-4b53-9921-01e351354bb4┌─res─────────┐│ abc$$$def$$ │└─────────────┘ |
position(haystack, needle[, start_pos])等价于locate(haystack, needle[, start_pos]) | 如果找到子字符串,则返回以字节为单位的起始位置(从 1 开始计数)。如果未找到子字符串, 返回 0。 | SELECT position('1234567890', '7') AS resQuery id: 80b88f03-d49e-40f6-b2f5-e4352adaee80┌─res─┐│ 7 │└─────┘ |
match(haystack, pattern) | 字符串正则匹配。如果匹配则返回 1,如果不匹配则返回 0。正则表达式不能包含空字节。对于在字符串中搜索子字符串的模式,最好使用 LIKE 或“位置”,因为它们的工作速度更快。 | SELECT match('abcccddd', '(^abc)') AS resQuery id: f912bd49-d01e-4caf-967e-4de7df38181f┌─res─┐│ 1 │└─────┘ |
like(haystack, pattern) | 检查字符串是否匹配简单的正则表达式。正则表达式可以包含元符号 % 和 _。% 表示任意数量的任意字节(包括零个字符)。_ 表示任何一个字节。使用反斜杠 \\ 转义元符号。 | SELECT 'abc' LIKE 'a%' AS re1, '12345' LIKE '__3__' AS res2Query id: f1c49f53-a27c-43c6-b298-246fc242a115┌─re1─┬─res2─┐│ 1 │ 1 │└─────┴──────┘ |
countMatches(haystack, pattern) | 返回字符串 haystack 中模式 pattern 的正则表达式匹配数。 | SELECT countMatches('foobar.com', 'o+') AS res1, countMatches('aaaa', 'aa') AS res2Query id: 151e1c67-4562-4711-a30a-f34d7e143469┌─res1─┬─res2─┐│ 2 │ 2 │└──────┴──────┘ |
1.5. 条件分支函数
程序的三大控制结构为:顺序结构、循环结构、分支结构(选择结构)。顺序结构的程序虽然能解决计算、输出等问题,但不能做判断再选择。对于要先做判断再选择的问题就要使用分支结构。在各大编程语言中,都有类似 if,case-when 的分支语句。在 ClickHouse 中则是用条件函数(Conditional Function)if()、multiIf() 来实现分支逻辑的选择。常用清单如下。
函数 | 功能说明 | 使用实例 |
if(cond, then, else) | 单条件分支函数。参数说明:cond - 判断条件。类型为 UInt8、Nullable(UInt8) 或 NULL。then - 满足条件时,返回的表达式。else - 不满足条件时,返回的表达式。 | SELECT if(2 > 1, 1, 0) AS res1, if(1 > 2, 1, 0) AS res2Query id: 51a72550-928d-4b51-b07b-f59a59ce1905┌─res1─┬─res2─┐│ 1 │ 0 │└──────┴──────┘ |
multiIf(cond_1, then_1, cond_2, then_2, ..., else) | 多条件分支函数。 | SELECT multiIf(2 > 10, 'c1', 1 > 2, 'c2', 2 > 1, 'c3', NULL) AS resQuery id: 248137af-179a-43fe-83df-c6493930b5af┌─res─┐│ c3 │└─────┘ |
NULL 值条件判断 | 条件判断中有 NULL 值的,结果仍然是 NULL。因此,针对类型为“Nullable”的字段,需要仔细检查查询条件。 | SELECT NULL < 1, 2 < NULL, NULL < NULL, NULL = NULLQuery id: 72059d3d-7e7e-45e6-8728-49752c932f56┌─less(NULL, 1)─┬─less(2, NULL)─┬─less(NULL, NULL)─┬─equals(NULL, NULL)─┐│ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │└───────────────┴───────────────┴──────────────────┴────────────────────┘ |
1.6. 时间计算函数
在人类的问题清单中,时间是一个重要的变量因素。计算机是用来解决人类问题的,故几乎在所有的编程语言、软件系统中,都有需要处理日期和时间的场景。在 ClickHouse 中专门设计了日期和时间类型,并提供了处理日期和时间的函数。
所有用于处理日期和时间的函数,都可以接受第二个可选的时区参数。例如:Asia/Shanghai,在这种情况下,他们使用指定的时区而不是本地时区(默认时区)。清单如下。
函数 | 功能说明 | 使用实例 |
now() | 计算当前时间。 | SELECT now()Query id: 5f12bb7e-31ce-4190-aeeb-ed7ba7c1e7aa┌───────────────now()─┐│ 2022-03-15 18:18:58 │└─────────────────────┘ |
today() | 计算今天日期,与 toDate(now()) 逻辑等价。 | SELECT today()Query id: 3a5c132c-3a0c-475d-a59c-b5e692cb2c09┌────today()─┐│ 2022-03-15 │└────────────┘ |
yesterday() | 计算昨天日期,与 today() - 1 逻辑等价。 | SELECT yesterday()Query id: 5ec0a7fc-1843-42d0-ae3a-1a1c7901e51f┌─yesterday()─┐│ 2022-03-15 │└─────────────┘ |
timeZone() | 计算当前时区。 | SELECT timeZone()Query id: c082e992-4678-4803-9c7d-2b69958bcd61┌─timeZone()────┐│ Asia/Shanghai │└───────────────┘ |
timeZoneOf(value) | 计算 DateTime/DateTime64 时间对象 value 的时区名称。 | SELECT timezoneOf(now())Query id: 288b5e11-d6a1-4ae1-a144-0510e6761da1┌─timezoneOf(now())─┐│ Asia/Shanghai │└───────────────────┘ |
toTimezone(value, timezone) | 将时间或日期和时间转换为指定的时区的时间。 | SELECT toDateTime('2022-03-15 00:00:00', 'UTC') AS time_utc, toTypeName(time_utc) AS type_utc, toTimeZone(time_utc, 'Asia/Shanghai') AS time_shanghaiFORMAT VerticalQuery id: 4f586c94-d555-4ecf-aff4-e60529016e95Row 1:──────time_utc: 2022-03-15 00:00:00type_utc: DateTime('UTC')time_shanghai: 2022-03-15 08:00:00 |
toYear(x) | 将日期或带时间的日期转换为包含 公元后年号 (AD) 的 UInt16 数字。 | SELECT now() AS x, toYear(x) AS yQuery id: c6bc02da-890b-4834-a00e-12426c849c7a┌───────────────────x─┬────y─┐│ 2022-03-15 18:21:03 │ 2022 │└─────────────────────┴──────┘ |
toQuarter(x) | 将日期或带时间的日期转换为包含季度数字的 UInt8 数字。 | SELECT now() AS x, toQuarter(x) AS yQuery id: bf25ccff-20ca-4d4d-8a6e-a04ec96953ce┌───────────────────x─┬─y─┐│ 2022-03-15 18:41:35 │ 1 │└─────────────────────┴───┘ |
toMonth(x) | 将日期或带时间的日期转换为包含月份编号 (1-12) 的 UInt8 数字。 | SELECT now() AS x, toMonth(x) AS yQuery id: 8fd85e2e-e4e7-4720-a9c6-a1957184147b┌───────────────────x─┬─y─┐│ 2022-03-15 18:41:06 │ 3 │└─────────────────────┴───┘ |
toDayOfYear(x) | 将日期或带时间的日期转换为 UInt16 数字,其中包含一年中的日期 (1-366)。 | SELECT now() AS x, toDayOfYear(x) AS yQuery id: cb7852f4-450b-48a2-9e0c-59b59e3884a4┌───────────────────x─┬──y─┐│ 2022-03-15 18:39:22 │ 74 │└─────────────────────┴────┘ |
toDayOfMonth(x) | 将日期或带时间的日期转换为 UInt8 数字,其中包含月份中的天数 (1-31)。 | SELECT now() AS x, toDayOfMonth(x) AS yQuery id: fbc39c16-5e2c-494b-97f4-a438999300b5┌───────────────────x─┬──y─┐│ 2022-03-15 18:38:43 │ 15 │└─────────────────────┴────┘ |
toDayOfWeek(x) | 将日期或带时间的日期转换为包含星期几的 UInt8 数字(星期一为 1,星期日为 7)。 | SELECT now() AS x, toDayOfWeek(x) AS yQuery id: cfe6fbb1-db7a-42ec-a00a-f28a2e737da8┌───────────────────x─┬─y─┐│ 2022-03-15 18:38:07 │ 2 │└─────────────────────┴───┘ |
toHour(x) | 将带时间的日期转换为包含 24 小时制小时数 (0-23) 的 UInt8 数字。 | SELECT now() AS x, toHour(x) AS yQuery id: 7bd0e1e8-43df-445c-8a23-1bc36e79f155┌───────────────────x─┬──y─┐│ 2022-03-15 18:36:48 │ 18 │└─────────────────────┴────┘ |
toMinute(x) | 将带时间的日期转换为小 以上是关于ClickHouse 函数极简教程的主要内容,如果未能解决你的问题,请参考以下文章 ClickHouse 实战:ClickHouse 基础数据类型极简教程 ClickHouse 实战:ClickHouse 高级数据类型极简教程 |