ClickHouse数组的相关操作函数,⼀⽹打尽
楔⼦
在⼀般的关系型数据库,相信很多⼈都不怎么使⽤数组这个结构,如果真的需要数组,那么会选择将其变成数组格式的字符串进⾏存储。但在 ClickHouse 中,数组的使⽤频率是⾮常⾼的,因为它内置了⼤量和数组有关的函数。
SELECT version();
/*
┌─version()─┐
│ 21.7.3.14 │
└───────────┘
*/
SELECT count() FROM system.functions WHERE name LIKE '%array%';
/*
┌─count()─┐
│      48 │
└─────────┘
*/
当前的 ClickHouse 是 21.7.3.14 版本,关于数组的函数有 48 个,通过这个 48 个函数,我们可以对数组进⾏各种骚操作。当然也有⼀些函数不是专门针对数组的,但是可以⽤在数组⾝上,我们就也放在⼀起说了,下⾯就来依次介绍相关函数的⽤法。empty:判断数组是否为空,如果⼀个数组不包含任何元素,返回 1;否则返回 0
SELECT empty([1, 2, 3]), empty([]);
/*
┌─empty([1, 2, 3])─┬─empty(array())─┐
│                0 │              1 │
└──────────────────┴────────────────┘
*/
empty 不仅可以检测数组是否为空,还可以检测字符串。
SELECT empty('satori'), empty('');
/*
┌─empty('satori')─┬─empty('')─┐
│              0 │        1 │
└─────────────────┴───────────┘
*/
notEmpty:判断数组是否不为空,如果⼀个数组包含⾄少⼀个元素,返回 1;不包含任何元素,则返回 0
SELECT notEmpty([1, 2, 3]), notEmpty([]);
/*
┌─notEmpty([1, 2, 3])─┬─notEmpty(array())─┐
│                  1 │                0 │
└─────────────────────┴───────────────────┘
*/
-- 同样可以作⽤于字符串
SELECT notEmpty('satori'), notEmpty('')
/*
┌─notEmpty('satori')─┬─notEmpty('')─┐
│                  1 │            0 │
└────────────────────┴──────────────┘
*/
length:返回数组的长度,该函数也可以返回字符串的长度
SELECT length([]), length([1, 2, 3]), length('satori'), length('');
/*
┌─length(array())─┬─length([1, 2, 3])─┬─length('satori')─┬─length('')─┐
│              0 │                3 │                6 │          0 │
└─────────────────┴───────────────────┴──────────────────┴────────────┘
*/
emptyArrayUInt8、emptyArrayUInt16、emptyArrayUInt32、emptyArrayUInt64、emptyArrayInt8、emptyArrayInt16、emptyArrayInt32、emptyArrayInt64、emptyArrayFloat32、emptyArrayFloat64、emptyArrayDate、emptyArrayDateTime、emptyArrayString:创建⼀个指定类型的空数组
-- 数组元素的类型为 nothing,因为没有指定任何元素
SELECT [] v, toTypeName(v);
/*
┌─v──┬─toTypeName(array())─┐
│ [] │ Array(Nothing)      │
└────┴─────────────────────┘
*/
-- 采⽤最⼩类型存储,因为 1 和 2 都在 UInt8 的范围内
SELECT [1, 2] v, toTypeName(v);
/*
┌─v─────┬─toTypeName([1, 2])─┐
│ [1,2] │ Array(UInt8)      │
└───────┴────────────────────┘
*/
-- 但是我们可以创建指定类型的数组
SELECT emptyArrayDateTime() v, toTypeName(v);
/*
┌─v──┬─toTypeName(emptyArrayDateTime())─┐
│ [] │ Array(DateTime)                  │
└────┴──────────────────────────────────┘
*/
range:类似于 Python 中的 range,看测试⽤例
array:也是创建⼀个数组,和直接使⽤⽅括号类似。但是 array 函数要求必须⾄少传递⼀个常量,否则就不知道要创建哪种类型的数组。如果想创建指定类型的空数组,那么使⽤上⾯的 emptyArray* 系列函数即可
-- 不管是使⽤ array 创建,还是使⽤ [] 创建,⾥⾯的元素都必须具有相同的类型,或者能够兼容
SELECT array(1, 2, 3), [1, 2, 3]
/*
┌─array(1, 2, 3)─┬─[1, 2, 3]─┐
│ [1,2,3]        │ [1,2,3]  │
└────────────────┴───────────┘
*/
arrayConat:将多个数组进⾏合并,得到⼀个新的数组
-- SELECT 中起的别名可以被直接其它字段所使⽤
SELECT [1, 2, 3] v1, [11, 22, 33] v2, [111, 222, 333] v3, arrayConcat(v1, v2, v3);
/*
字符串长度函数是什么
┌─v1────┬─v2──────┬─v3────────┬─arrayConcat([1, 2], [11, 22], [111, 222])─┐
│ [1,2] │ [11,22] │ [111,222] │ [1,2,11,22,111,222]                      │
└───────┴─────────┴───────────┴───────────────────────────────────────────┘
*/
arrayElement:查指定索引的元素,索引从 1 开始,也可以通过⽅括号直接取值;另外也⽀持负数索引,-1 代表最后⼀个元素
-- 索引从 1 开始,所以 arr[20] 就表⽰第 20 个元素,也就是 19
WITH range(100) AS arr SELECT arrayElement(arr, 20), arr[20];
/*
┌─arrayElement(arr, 20)─┬─arrayElement(arr, 20)─┐
│                    19 │                    19 │
└───────────────────────┴───────────────────────┘
*/
WITH range(100) AS arr SELECT arrayElement(arr, -1), arr[-50];
/*
┌─arrayElement(arr, -1)─┬─arrayElement(arr, -50)─┐
│                    99 │                    50 │
└───────────────────────┴────────────────────────┘
*/
has:判断数组⾥⾯是否包含某个元素,如果包含,返回 1;不包含,返回0
WITH [1, 2, Null] AS arr SELECT has(arr, 2), has(arr, 0), has(arr, Null);
/
*
┌─has(arr, 2)─┬─has(arr, 0)─┬─has(arr, NULL)─┐
│          1 │          0 │              1 │
└─────────────┴─────────────┴────────────────┘
*/
-- 嵌套数组也是可以的
SELECT has([[1, 2]], [1, 2]);
/*
┌─has([[1, 2]], [1, 2])─┐
│                    1 │
└───────────────────────┘
*/
hasAll:判断数组⾥⾯是否包含某个⼦数组,如果包含,返回 1;不包含,返回0
注意:空数组是任意数组的⼦集;Null 会被看成是普通的值;数组中的元素顺序没有要求;1.0 和 1 被视为相等
hasAll([], []):返回 1
hasAll([1, Null], [Null]):返回 1
hasAll([1.0, 2.0, 3.0], [2.0, 3.0, 1.0]):返回 1,因为元素顺序⽆影响,并且 1.0 和 1 被视为相等
hasAll(['a', 'b'], ['a']):返回 1
hasAll(['a', 'b'], ['c']):返回 0
hasAll([[1, 2], [3, 4]], [[1, 2], [3, 4]]):返回 1,嵌套数组也是可以的
在 has 函数⾥⾯也有嵌套数组,但是维度不同。⽐如 has(a, b):如果 a 是维度为 N 的数组,那么 b 必须是维度为 N - 1 的数组;⽽ hasAll 则要求 a 和 b 的维度必须相同。
WITH [[1, 2], [11, 22]] AS arr, [[1, 2], [11, 22]] AS subset SELECT hasAll(arr, subset)
/*
┌─hasAll(arr, subset)─┐
│                  1 │
└─────────────────────┘
*/
-- 我们说 SELECT ⾥⾯别名可以给其它字段使⽤,因此下⾯这种做法也是合法的
WITH [[1, 2], [11, 22]] AS arr, arr AS subset SELECT hasAll(arr, subset)
/*
┌─hasAll(arr, subset)─┐
│                  1 │
└─────────────────────┘
*/
hasAny:判断两个数组⾥⾯是否有相同的元素,只要有 1 个相同的元素,返回 1;否则,返回 0
SELECT hasAny([1.0, 2.0], [1]), hasAny([Null], [1, Null])
/*
┌─hasAny([1., 2.], [1])─┬─hasAny([NULL], [1, NULL])─┐
│                    1 │                        1 │
└───────────────────────┴───────────────────────────┘
*/
SELECT hasAny([[1, 2], [3, 4]], [[3, 4]])
/*
┌─hasAny([[1, 2], [3, 4]], [[3, 4]])─┐
│                                  1 │
└────────────────────────────────────┘
*/
hasSubstr:和 hasAll 类似,但是顺序有要求,hasAll(arr, subset) 要求的是 subset 中的元素在 arr 中都出现即可;但是hasSubstr 函数则不仅要求 subset 中的元素在 arr 中都出现,并且还要以相同的顺序。举个栗⼦:
hasSubstr([1, 2, 3], [2, 3]):返回 1
hasSubstr([1, 2, 3], [3, 2]):返回 0
hasSubstr([[1, 2], [2, 1], [3, 2]], [[3, 2]]):返回 1
-- 两个数组的维度必须相同
SELECT hasSubstr([1, 2, 3], [3, 2]), hasSubstr([1, 2, 3], [2, 3]);
/*
┌─hasSubstr([1, 2, 3], [3, 2])─┬─hasSubstr([1, 2, 3], [2, 3])─┐
│                            0 │                            1 │
└──────────────────────────────┴──────────────────────────────┘
*/
indexOf:查某个元素第⼀次在数组中出现的位置,索引从 1 开始;如果不存在,则返回 0

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。