暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Apache Doris 数组类型探索及使用

1619

1. 概述

大家在之前使用Hive的时候知道 Hive 支持复杂的数据类型,Array、Map、STRUCT,特别是数组类型,在实际的开发和使用中经常遇到,而且使用也非常方便,Doris 在即将发布的 1.2 版本中也将提供数据类型的支持。

这里我们通过编译 master 分支的源码,提前尝鲜 Doris 的数组类型

具体 Doris 的编译方法请参照官方文档:https://doris.apache.org/zh-CN/docs/install/source-install/compilation

注意:

这里编译master分之的使用 docker pull apache/doris:build-env-ldb-toolchain-latest
Docker 镜像

具体的安装部署参照:https://doris.apache.org/zh-CN/docs/install/install-deploy


2. 开始使用

默认 Doris 数据现在是禁用的,我们需要通过下面的开关来设置启用

MySQL [(none)]> set enable_array_type=true;
Query OK, 0 rows affected (0.00 sec)

2.1 ARRAY<T>类型介绍

由T类型元素组成的数组,不能作为key列使用。目前支持在Duplicate模型的表中使用。其他数据模型暂时不支持

T支持的类型有:

BOOLEAN, 
TINYINT,
SMALLINT,
INT,
BIGINT,
LARGEINT,
FLOAT,
DOUBLE,
DECIMAL,
DATE,
DATETIME,
CHAR,
VARCHAR,
STRING

2.2 创建表

这里我们创建一张班级测试表,里面包含class_id,class_name,student_ids(学生ID数组)

create table class_test(
class_id int ,
class_name varchar(20),
student_ids array<int>
) ENGINE=OLAP
DUPLICATE KEY(`class_id`,class_name)
COMMENT "OLAP"
DISTRIBUTED BY HASH(`class_name`) BUCKETS 2
PROPERTIES (
"replication_allocation" = "tag.location.default: 1",
"in_memory" = "false",
"storage_format" = "V2"
);

2.3 数据导入

这里导入的数组数据,数组列可以允许为NULL,数组内部元素也允许为NULL

2.3.1 Insert 数据插入:

MySQL [demo]> insert into class_test values (10001,'一年级一班',[10001, 10002, 10003, 10004, 10005, 10006, 10007, 10008, 10009, 10010, 10011]);
Query OK, 1 row affected (0.03 sec)
{'label':'insert_fd3e529fed2e4a36-81e63bc50f2280ca', 'status':'VISIBLE', 'txnId':'7'}
MySQL [demo]> insert into class_test values (10002,'一年级二班',[20001, 20002, 20003, 20004, 20005, 20006, 20007, 20008, 20009, 20010, 20012]);
Query OK, 1 row affected (0.04 sec)
{'label':'insert_cda2c5d9305d457a-a46027b50d32eae3', 'status':'VISIBLE', 'txnId':'8'}
MySQL [demo]> insert into class_test values (10004,'四年级二班',[10002,null,10003]),(10005,'四年级三班',[10005,null,null]);
Query OK, 2 rows affected (0.04 sec)
{'label':'insert_a414305ef7494f34-9b89f058886080ec', 'status':'VISIBLE', 'txnId':'5'}

2.3.2 Stream load 导入数据

示例数据如下,保存成csv,这里使用tab分隔符

10006 六年级一班 [60002,60002,60003,null,60005]
10007 六年级二班 [70002,60002,70003,null,70005]
10008 六年级三班 [80002,60002,80003,null,80005]

导入

curl --location-trusted -u root: -T test.csv -H "column_separator:\t" -H "label:class_test" http://localhost:8030/api/demo/class_test/_stream_load


{
   "TxnId": 9,
   "Label": "class_test",
   "TwoPhaseCommit": "false",
   "Status": "Success",
   "Message": "OK",
   "NumberTotalRows": 3,
   "NumberLoadedRows": 3,
   "NumberFilteredRows": 0,
   "NumberUnselectedRows": 0,
   "LoadBytes": 159,
   "LoadTimeMs": 65,
   "BeginTxnTimeMs": 7,
   "StreamLoadPutTimeMs": 22,
   "ReadDataTimeMs": 0,
   "WriteDataTimeMs": 8,
   "CommitAndPublishTimeMs": 27
}

2.4 查询数据

2.4.1 查看所有数据

MySQL [demo]> select * from class_test;
+----------+-----------------+-------------------------------------------------------------------------------+
| class_id | class_name     | student_ids                                                                   |
+----------+-----------------+-------------------------------------------------------------------------------+
|   10004 | 四年级二班     | [10002, NULL, 10003]                                                         |
|   10005 | 四年级三班     | [10005, NULL, NULL]                                                           |
|   10001 | 一年级一班     | [10001, 10002, 10003, 10004, 10005, 10006, 10007, 10008, 10009, 10010, 10011] |
|   10002 | 一年级二班     | [20001, 20002, 20003, 20004, 20005, 20006, 20007, 20008, 20009, 20010, 20012] |
|   10006 | 六年级一班     | [60002, 60002, 60003, NULL, 60005]                                           |
|   10007 | 六年级二班     | [70002, 60002, 70003, NULL, 70005]                                           |
|   10008 | 六年级三班     | [80002, 60002, 80003, NULL, 80005]                                           |
+----------+-----------------+-------------------------------------------------------------------------------+
7 rows in set (0.00 sec)

2.4.2 计算student_ids 数组里的最大值

MySQL [demo]> select class_id, ARRAY_MAX(student_ids) from class_test;
+----------+--------------------------+
| class_id | array_max(`student_ids`) |
+----------+--------------------------+
|    10001 |                    10012 |
|    10002 |                    20012 |
+----------+--------------------------+
2 rows in set (0.02 sec)

2.4.3 判断字段student_ids是否包含10002

MySQL [demo]> select class_id, array_contains(student_ids,10002) from class_test;
+----------+--------------------------------------+
| class_id | array_contains(`student_ids`, 10002) |
+----------+--------------------------------------+
|    10001 |                                    1 |
|    10002 |                                    0 |
+----------+--------------------------------------+
2 rows in set (0.01 sec)

2.4.4 返回一个数组,包含所有在student_ids内但不在[10012,20011]内的元素,不包含重复项

MySQL [demo]> select class_id, array_except(student_ids,[10012,20011]) from class_test ;
+----------+-------------------------------------------------------------------------------+
| class_id | array_except(`student_ids`, ARRAY(10012, 20011))                              |
+----------+-------------------------------------------------------------------------------+
|    10001 | [10001, 10002, 10003, 10004, 10005, 10006, 10007, 10008, 10009, 10010, 10011] |
|    10002 | [20001, 20002, 20003, 20004, 20005, 20006, 20007, 20008, 20009, 20010, 20012] |
+----------+-------------------------------------------------------------------------------+
2 rows in set (0.01 sec)


2.4.5 查找10002 在student_ids 数组中第一次出现的位置/索引

MySQL [demo]> select class_id,array_position(student_ids, 10002) from class_test ;
+----------+--------------------------------------+
| class_id | array_position(`student_ids`, 10002) |
+----------+--------------------------------------+
|    10001 |                                    2 |
|    10002 |                                    0 |
+----------+--------------------------------------+
2 rows in set (0.01 sec)

2.4.6 去除student_ids数组中第二个值


-- 查找10002 在student_ids 数组中第一次出现的位置/索引
MySQL [demo]> select class_id,array_position(student_ids, 10002) from class_test ;
+----------+--------------------------------------+
| class_id | array_position(`student_ids`, 10002) |
+----------+--------------------------------------+
|    10001 |                                    2 |
|    10002 |                                    0 |
+----------+--------------------------------------+
2 rows in set (0.01 sec)

2.4.7 取出student_ids数组中第二个值

MySQL [demo]> select class_id,element_at(student_ids, 2) from class_test ;
+----------+------------------------------+
| class_id | element_at(`student_ids`, 2) |
+----------+------------------------------+
|    10001 |                        10002 |
|    10002 |                        20002 |
+----------+------------------------------+
2 rows in set (0.01 sec)

2.4.8 获取student_ids 数组长度

MySQL [demo]> select class_id,size(student_ids) from class_test ;
+----------+---------------------+
| class_id | size(`student_ids`) |
+----------+---------------------+
|    10001 |                  12 |
|    10002 |                  12 |
+----------+---------------------+
2 rows in set (0.01 sec)

--返回删除指定元素后的数组

MySQL [demo]> select class_id,array_remove(student_ids,10003) from class_test ;
+----------+--------------------------------------------------------------------------------------+
| class_id | array_remove(`student_ids`, 10003)                                                   |
+----------+--------------------------------------------------------------------------------------+
|    10001 | [10001, 10002, 10004, 10005, 10006, 10007, 10008, 10009, 10010, 10011, 10012]        |
|    10002 | [20001, 20002, 20003, 20004, 20005, 20006, 20007, 20008, 20009, 20010, 20011, 20012] |
+----------+--------------------------------------------------------------------------------------+
2 rows in set (0.01 sec)

2.4.9 返回删除指定元素后的数组

MySQL [demo]> insert into class_test values (10004,'四年级二班',[10002,null,10003]),(10005,'四年级三班',[10005,null,null]);
Query OK, 2 rows affected (0.04 sec)
{'label':'insert_a414305ef7494f34-9b89f058886080ec', 'status':'VISIBLE', 'txnId':'5'}

2.5 数组列转行操作

我们在实际使用过程中,有很多行列转换的需求:行转列,列转行。下面我们来看看 Doris 中数组类型如何进行行列转换。

2.5.1 列转行

我们讲讲每个学生ID和班级ID,班级名称单独组成一行数据。

MySQL [demo]> SELECT class_id,class_name,student_id FROM class_test LATERAL VIEW explode(`student_ids`) student_id AS student_id WHERE class_id='10001';
+----------+-----------------+------------+
| class_id | class_name      | student_id |
+----------+-----------------+------------+
|    10001 | 一年级一班      |      10001 |
|    10001 | 一年级一班      |      10002 |
|    10001 | 一年级一班      |      10003 |
|    10001 | 一年级一班      |      10004 |
|    10001 | 一年级一班      |      10005 |
|    10001 | 一年级一班      |      10006 |
|    10001 | 一年级一班      |      10007 |
|    10001 | 一年级一班      |      10008 |
|    10001 | 一年级一班      |      10009 |
|    10001 | 一年级一班      |      10010 |
|    10001 | 一年级一班      |      10011 |
+----------+-----------------+------------+
11 rows in set (0.00 sec)

2.5.2 行转列

这里我们讲每个学生所在的班级统一转成一个数组

MySQL [demo]> SELECT `stud_id`,collect_list(`class_name`) AS classes FROM     (SELECT `class_name`,stud_id      FROM `class_test` LATERAL VIEW explode(`student_ids`) stud_id AS stud_id) AS array_demo_row GROUP BY `stud_id`;
+---------+------------------------------------------------------------------------------------------------+
| stud_id | classes                                                                                        |
+---------+------------------------------------------------------------------------------------------------+
|   20010 | ['一年级二班']                                                                                  |
|   20012 | ['一年级二班']                                                                                  |
|   10011 | ['一年级一班']                                                                                  |
|   70005 | ['六年级二班']                                                                                  |
|   70003 | ['六年级二班']                                                                                  |
|   10003 | ['四年级二班', '一年级一班']                                                                     |
|   10005 | ['四年级三班', '一年级一班']                                                                     |
|   20002 | ['一年级二班']                                                                                  |
|   20004 | ['一年级二班']                                                                                  |
|   70002 | ['六年级二班']                                                                                  |
|   10010 | ['一年级一班']                                                                                  |
|   10004 | ['一年级一班']                                                                                  |
|   10002 | ['四年级二班', '一年级一班']                                                                     |
|   20005 | ['一年级二班']                                                                                  |
|   20003 | ['一年级二班']                                                                                  |
|   10009 | ['一年级一班']                                                                                  |
|   80002 | ['六年级三班']                                                                                  |
|   60003 | ['六年级一班']                                                                                  |
|   20008 | ['一年级二班']                                                                                  |
|   60005 | ['六年级一班']                                                                                  |
|   20006 | ['一年级二班']                                                                                  |
|   10001 | ['一年级一班']                                                                                  |
|   10007 | ['一年级一班']                                                                                  |
|   80005 | ['六年级三班']                                                                                  |
|   80003 | ['六年级三班']                                                                                  |
|   10008 | ['一年级一班']                                                                                  |
|   60002 | ['六年级一班', '六年级一班', '六年级二班', '六年级三班']                                            |
|   20009 | ['一年级二班']                                                                                  |
|   20007 | ['一年级二班']                                                                                  |
|   20001 | ['一年级二班']                                                                                  |
|   10006 | ['一年级一班']                                                                                  |
|    NULL | ['四年级二班', '四年级三班', '四年级三班', '六年级一班', '六年级二班', '六年级三班']                   |
+---------+------------------------------------------------------------------------------------------------+
32 rows in set (0.01 sec)

3. Doris 数组函数介绍

Doris 针对数据类型提供了丰富的函数支持,下面我们简单介绍Doris的数组函数,具体可以参考官方文档(release之后再官网可以看到)

函数名称函数介绍备注
ARRAY_AVG返回数组中所有元素的平均值
ARRAY_CONTAINS判断数组中是否包含value,1表示包含,0表示不包含仅支持向量化引擎中使用
ARRAY_DISTINCT数组去重仅支持向量化引擎中使用
ARRAY_EXCEPT是否包含所有在array1内但不在array2内的元素仅支持向量化引擎中使用
ARRAY_INTERSECT包含array1和array2的交集中的所有元素仅支持向量化引擎中使用
ARRAY_MAX返回数组中最大的元素
ARRAY_MIN返回数组中最小的元素
ARRAY_POSITION返回value
在数组中第一次出现的位置/索引
仅支持向量化引擎中使用
ARRAY_PRODUCT返回数组中所有元素的乘积
ARRAY_REMOVE返回移除所有的指定元素后的数组仅支持向量化引擎中使用
ARRAY_SORT返回按升序排列后的数组仅支持向量化引擎中使用
ARRAY_SUM返回数组中所有元素之和
ARRAY_UNION返回一个数组,包含array1和array2的并集中的所有元素,不包含重复项仅支持向量化引擎中使用
ARRAYS_OVERLAP判断left和right数组中是否包含公共元素仅支持向量化引擎中使用
ELEMENT_AT返回数组中位置为 position
的元素
仅支持向量化引擎中使用
SIZE (CARDINALITY)返回数组中元素数量仅支持向量化引擎中使用

配合数组行列转换的函数

函数名称函数说明备注
EXPLODE表函数,需配合 Lateral View 使用 ,将 ARRAY 列展开成多行
EXPLODE_OUTER表函数,需配合 Lateral View 使用 ,将 ARRAY 列展开成多行
COLLECT_LISTARRAY 行转列仅支持向量化引擎中使用
COLLECT_SETARRAY 行转列仅支持向量化引擎中使用

4. 总结

这里我们简单介绍了 Doris 在 8 月底 9 月初即将发的 1.2 版本对数组类型的支持,通过写简单的示例给大家讲解了怎么使用 Doris 的数组类型,大家可以期待一下,新版本的发布,如果你想提前尝鲜,也可以自己编译 master 分支(强烈不建议在生产环境使用这个分支编译的版本),后续我们也将会出去其他复杂数据类型的支持,敬请期待。

文章转载自锋哥聊DORIS数仓,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论