ClickHouse的应用
详情访问主页:
https://clickhouse.tech/docs/v20.3/en/
- 启动服务端
service clickhouse-server start
- 启动交互式客户端
clickhouse-client
支持多行输出:
clickhouse-client -m
一 数据库和表
1.数据库的操作
- 创建数据库
create database db_name;
- 切换数据库
use db_name;
- 查看当前数据库
select currentDatabase();
- 删除数据库
drop database db_doit19;
2.表
- ClickHouse在建表需要制定引擎
- 注意大小写(数据类型和引擎类型)
- 数据存储位置:
![image-20201205163752895](https://gitee.com/abin571/abinde-ty/raw/master/img/image-20201205163752895.png)
二 Log引擎
1.Log
日志与 TinyLog 的不同之处在于,«标记» 的小文件与列文件存在一起。这些标记写在每个数据块上,并且包含偏移量,这些偏移量指示从哪里开始读取文件以便跳过指定的行数。这使得可以在多个线程中读取表数据。对于并发数据访问,可以同时执行读取操作,而写入操作则阻塞读取和其它写入。Log 引擎不支持索引。同样,如果写入表失败,则该表将被破坏,并且从该表读取将返回错误。Log 引擎适用于临时数据,write-once 表以及测试或演示目的。
-
建表
create table testolog ( id Int16, name String, age UInt8, gender Enum('男'=1,'女'=2)) engine=Log;
-
导入数据
insert into testolog values(1,'guodegang',50,'男'); insert into testolog values(2,'yuqian',51,'男'); insert into testolog values(3,'高峰',55,'男');
多次插入的数据会分开显示
2.TinyLog引擎(数据不分块)
最简单的表引擎,用于将数据存储在磁盘上。每列都存储在单独的压缩文件中,写入时,数据将附加到文件末尾。
该引擎没有并发控制
**- 只支持并发读**
- 如果同时从表中读取和写入数据,则读取操作将抛出异常;
- 如果同时写入多个查询中的表,则数据将被破坏。
- 启动服务端
service clickhouse-server start
- 启动交互式客户端
clickhouse-client -m
- 建库
create database db_user;
- 建表
create table tb_TinyLog(
id UInt8,
name String,
score Float64
)
engine = TinyLog;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y8461F1L-1607486606040)(https://gitee.com/abin571/abinde-ty/raw/master/img/20201204195631716.png)]
- 添加数据
insert into tb_TinyLog values(1,'zss','98.3'),(1,'lss','80.5');
- 再次添加数据
insert into tb_TinyLog values(3,'ww',70.6);
- 数据存储位置
data.bin — 数据文件。
index.mrk — 带标记的文件。标记包含了已插入的每个数据块中每列的偏移量。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jejdlvjg-1607486606046)(https://gitee.com/abin571/abinde-ty/raw/master/img/20201204195631716.png)]
3.StripeLog(数据分块列在一起)
在你需要写入许多小数据量(小于一百万行)的表的场景下使用这个引擎。
StripeLog 引擎将所有列存储在一个文件中。*对每一次 Insert 请求,**ClickHouse* 将数据块追加在表文件的末尾,逐列写入。
StripeLog 引擎不支持 ALTER UPDATE 和 ALTER DELETE 操作。
- 建表
create table tb_StripeLog(
id UInt8,
name String,
score Float64
)
engine = StripeLog;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vNouKnFP-1607486606048)(https://gitee.com/abin571/abinde-ty/raw/master/img/20201204195631716.png)]
- 添加数据
insert into tb_StripeLog values(1,'zss','98.3'),(1,'lss','80.5');
- 再次添加数据
insert into tb_StripeLog values(3,'ww','50.6');
4.小结
-
共同属性
数据存储在磁盘上。
写入时将数据追加在文件末尾。
不支持突变操作。
不支持索引。
这意味着
SELECT
在范围查询时效率不高。非原子地写入数据。
如果某些事情破坏了写操作,例如服务器的异常关闭,你将会得到一张包含了损坏数据的表。
-
差异
Log 和 StripeLog 引擎支持:
-
并发访问数据的锁。
INSERT
请求执行过程中表会被锁定,并且其他的读写数据的请求都会等待直到锁定被解除。如果没有写数据的请求,任意数量的读请求都可以并发执行。 -
并行读取数据。
在读取数据时,ClickHouse 使用多线程。 每个线程处理不同的数据块。
Log 引擎为表中的每一列使用不同的文件。StripeLog 将所有的数据存储在一个文件中。因此 StripeLog 引擎在操作系统中使用更少的描述符,但是 Log 引擎提供更高的读性能。
TinyLog 引擎是该系列中最简单的引擎并且提供了最少的功能和最低的性能。TingLog 引擎不支持并行读取和并发数据访问,并将每一列存储在不同的文件中。它比其余两种支持并行读取的引擎的读取速度更慢,并且使用了和 Log 引擎同样多的描述符。你可以在简单的低负载的情景下使用它。
-
三 MergeTree家族引擎
1.简介
1.1 优势
MergeTree系列的表引擎是ClickHouse数据存储功能的核心。它们提供了用于弹性和高性能数据检索的大多数功能:列存储,自定义分区,稀疏的主索引,辅助数据跳过索引等。
-
基本MergeTree表引擎可以被认为是单节点ClickHouse实例的默认表引擎,因为它在各种用例中通用且实用。
-
对于生产用途,ReplicatedMergeTree是必经之路,因为它为常规MergeTree引擎的所有功能增加了高可用性。一个额外的好处是在数据提取时自动进行重复数据删除,因此如果插入过程中出现网络问题,该软件可以安全地重试。
1.2 劣势
MergeTree引擎的主要缺点是它们很重。因此,典型的模式是没有太多。如果您需要许多小表(例如用于临时数据),请考虑使用Log engine family。
1.3 特点
该MergeTree系列中的引擎旨在将大量数据插入表中。数据快速地逐个部分地写入表中,然后应用规则在后台合并这些部分。这种方法比插入期间连续重写存储中的数据效率更高。
- 存储按主键排序的数据。
这使您可以创建一个小的稀疏索引,以帮助更快地查找数据。
- 如果指定了分区键,则可以使用分区。
ClickHouse支持的某些分区操作比对相同数据,相同结果的常规操作更有效。ClickHouse还会自动切断在查询中指定了分区键的分区数据。这也提高了查询性能。
- 数据复制支持。
ReplicatedMergeTree表族提供数据复制。有关更多信息.
- 数据采样支持。
如有必要,可以在表中设置数据采样方法。
1.4 建表语句
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster](
name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1] [TTL expr1],
name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2] [TTL expr2],
...
INDEX index_name1 expr1 TYPE type1(...) GRANULARITY value1,
INDEX index_name2 expr2 TYPE type2(...) GRANULARITY value2 )
ENGINE = MergeTree()
ORDER BY expr
[PARTITION BY expr]
[PRIMARY KEY expr]
[SAMPLE BY expr]
[TTL expr [DELETE|TO DISK 'xxx'|TO VOLUME 'xxx'], ...]
[SETTINGS name=value, ...]
- ENGINE—引擎的名称和参数。ENGINE = MergeTree()。该MergeTree引擎没有参数。
- ORDER BY —排序键,列名称或任意表达式的元组。范例:ORDER BY (CounterID, EventDate)。
- 如果PRIMARY KEY子句未明确定义主键,则ClickHouse会将排序键用作主键。
- ORDER BY tuple()如果不需要排序,请使用语法。
- PARTITION BY— 分区键。可选的。
- 要按月进行分区,请使用toYYYYMM(date_column)表达式,其中的date_column是日期类型为Date的列。此处的分区名称具有"YYYYMM"格式。
- PRIMARY KEY—主键(与排序键不同)。可选的。
- 默认情况下,主键与排序键(由ORDER BY子句指定)相同。因此,在大多数情况下,无需指定单独的PRIMARY KEY子句。
SAMPLE BY—用于采样的表达式。可选的。
- 如果使用采样表达式,则主键必须包含它。范例:SAMPLE BY intHash32(UserID) ORDER BY (CounterID, EventDate, intHash32(UserID))。
TTL—规则列表,用于指定行的存储持续时间并定义磁盘和卷之间零件自动移动的逻辑。可选的。
- 结果必须有一个Date或一DateTime列。例:
TTL date + INTERVAL 1 DAY- 规则的类型DELETE|TO DISK ‘xxx’|TO VOLUME 'xxx’指定了在满足表达式(达到当前时间)时要对零件执行的操作:删除过期的行,将零件(如果对零件中的所有行都满足表达式)移动到指定的磁盘(TO DISK ‘xxx’)或到音量(TO VOLUME ‘xxx’)。规则的默认类型为删除(DELETE)。可以指定多个规则的列表,但最多只能有一个DELETE规则。
SETTINGS—控制MergeTree(可选)行为的其他参数:
index_granularity—索引标记之间的最大数据行数。默认值:8192。请参见数据存储。
index_granularity_bytes—数据粒度的最大大小(以字节为单位)。默认值:10Mb。要仅按行数限制颗粒大小,请设置为0(不建议)。请参阅数据存储。
enable_mixed_granularity_parts—启用或禁用过渡以通过index_granularity_bytes设置控制颗粒尺寸。在版本19.11之前,只有index_granularity用于限制颗粒大小的设置。index_granularity_bytes从具有大行(数十和数百MB)的表中选择数据时,此设置可提高ClickHouse性能。如果您的表具有大行,则可以为表启用此设置以提高SELECT查询效率。
use_minimalistic_part_header_in_zookeeper— ZooKeeper中数据部件头的存储方法。如果为use_minimalistic_part_header_in_zookeeper=1,则ZooKeeper存储的数据较少。有关更多信息,请参阅“服务器配置参数”中的设置说明。
min_merge_bytes_to_use_direct_io—使用对存储磁盘的直接I / O访问所需的最小合并操作数据量。合并数据部分时,ClickHouse会计算要合并的所有数据的总存储量。如果卷超过min_merge_bytes_to_use_direct_io字节,ClickHouse将使用直接I / O接口(O_DIRECT选项)读取数据并将数据写入存储磁盘。如果为min_merge_bytes_to_use_direct_io = 0,则直接I / O被禁用。默认值:10 * 1024 * 1024 * 1024字节。
merge_with_ttl_timeout—重复与TTL合并之前的最小延迟(以秒为单位)。默认值:86400(1天)。
write_final_mark—启用或禁用在数据部分的末尾(最后一个字节之后)写入最终索引标记。默认值:1.不要关闭它。
merge_max_bloCK_size—合并操作的块中的最大行数。默认值:8192
storage_policy—存储策略。请参阅使用多个块设备进行数据存储。
min_bytes_for_wide_part,min_rows_for_wide_part—可以以Wide格式存储的数据部分中的最小字节/行数。您可以设置这些设置之一,全部或全部。请参阅数据存储。
2.MergeTree引擎
-
建表
create table testoMTree( id String , name String , city String ) engine = MergeTree order by id;
-
导入二次数据(数据是分开的)
insert into testoMTree values('id004','wb','HB'),('id005','DHT','HN'),('id006','ZXX','HLJ'); insert into testoMTree values('id011','wb2','HB'),('id007','DHT2','HN'),('id016','ZXX2','HLJ');
-
本地文件格式
drwxr-x---. 2 clickhouse clickhouse 177 Dec 5 06:35 all_1_1_0 drwxr-x---. 2 clickhouse clickhouse 177 Dec 5 06:35 all_2_2_0 drwxr-x---. 2 clickhouse clickhouse 6 Dec 5 06:31 detached -rw-r-----. 1 clickhouse clickhouse 1 Dec 5 06:31 format_version.txt [root@linux01 testoMTree]# pwd /var/lib/clickhouse/data/default/testoMTree [root@linux01 testoMTree]# cd all_1_1_0/ [root@linux01 all_1_1_0]# ll total 40 -rw-r-----. 1 clickhouse clickhouse 320 Dec 5 06:35 checksums.txt -rw-r-----. 1 clickhouse clickhouse 36 Dec 5 06:35 city.bin -rw-r-----. 1 clickhouse clickhouse 48 Dec 5 06:35 city.mrk2 -rw-r-----. 1 clickhouse clickhouse 77 Dec 5 06:35 columns.txt -rw-r-----. 1 clickhouse clickhouse 1 Dec 5 06:35 count.txt -rw-r-----. 1 clickhouse clickhouse 42 Dec 5 06:35 id.bin -rw-r-----. 1 clickhouse clickhouse 48 Dec 5 06:35 id.mrk2 -rw-r-----. 1 clickhouse clickhouse 37 Dec 5 06:35 name.bin -rw-r-----. 1 clickhouse clickhouse 48 Dec 5 06:35 name.mrk2 -rw-r-----. 1 clickhouse clickhouse 12 Dec 5 06:35 primary.idx - *.bin是按列保存数据的文件 - *.mrk保存块偏移量 - primary.idx保存主键索引
-
每次添加数据,都会在表目录下生成一个文件夹
-
合并数据
optimize table tb_tree1;
-
注意:MergeTree引擎数据可以重复
3.ReplacingMergeTree
-
这个引擎是在 MergeTree 的基础上,添加了“处理重复数据”的功能,该引擎和MergeTree的不同之处在于它会删除具有相同主键 的重复项。
-
数据的去重只会在合并的过程中出现。
合并会在未知的时间在后台进行,所以你无法预先作出计划。有一些数据可能仍未被处理。因此,ReplacingMergeTree 适用于在后台清除重复的数据以节省空间,但是它不保证没有重复的数据出现。
(允许插入重复的数据,系统会在合并数据(optimize)时进行比较,保留最新的版本的数据。
使用演示:
-
建表/添加数据
create table testoRMTree( id String , name String , v UInt8 ) engine = ReplacingMergeTree(v) order by id; --v是自定义的一个版本列,用来记录数据版本 insert into testoRMTree values ('a001','zss',10),('a002','lss',10) ; insert into testoRMTree values ('a001','zss2',30),('a002','lss2',5) ; ┌─id───┬─name─┬──v─┐ │ a001 │ zss │ 10 │ │ a002 │ lss │ 10 │ └──────┴──────┴────┘ ┌─id───┬─name─┬──v─┐ │ a001 │ zss2 │ 30 │ │ a002 │ lss2 │ 5 │ └──────┴──────┴────┘
-
合并数据
optimize table testoRMTree; ┌─id───┬─name─┬──v─┐ │ a001 │ zss2 │ 30 │ │ a002 │ lss │ 10 │ └──────┴──────┴────┘ --保留了版本号v,最大的数据
-
可以使用时间作为比较字符
create table tb_replacing_tree2( id String , name String , accTime DateTime ) engine = ReplacingMergeTree(accTime) order by id ;
4.CollapsingMergeTree
- ClickHouse实现了CollapsingMergeTree来消除ReplacingMergeTree的限制(只删除小版本字段,只保留最大版本数据);
- 该引擎要求在建表语句中指定一个标记列Sign,后台Compaction时会将主键相同、Sign相反的行进行折叠,也即删除;
- 标记只能是1或-1;
使用演示:
-
建表/插入数据
create table testoCMTree( id String , name String , v UInt8, s Int8 ) engine = CollapsingMergeTree(s) order by id; insert into testoCMTree values ('a001','lss',2,1) ; insert into testoCMTree values ('a001','zss',1,1) ; insert into testoCMTree values ('a001','zss2',3,-1);
-
合并数据
个人理解:-1会抵消最先输入的同主键的数据。
CollapsingMergeTree一般不使用
5.VersionedCollapsingMergeTree
- 取消字段和数据版本同事使用,避免取消行数据无法删除的问题
- 为了解决CollapsingMergeTree乱序写入情况下无法正常折叠问题,VersionedCollapsingMergeTree表引擎在建表语句中新增了一列Version,用于在乱序情况下记录状态行与取消行的对应关系。
- 主键相同,且Version相同、Sign相反的行,在Compaction时会被删除。
- 此引擎和CollapsingMergeTree引擎,允许相同主键的数据同时存在(同MergeTree),所以要控制好版本号和标记符号,合理使用,避免数据混乱。
使用演示:
-
建表、添加数据
create table testoVCMTree( id UInt8 , name String , sign Int8, version UInt8 ) engine = VersionedCollapsingMergeTree(sign, version) order by id ;
-
插入数据
insert into testoVCMTree values(1,'a',1,1),(1,'b',1,2) ; insert into testoVCMTree values(1,'a2',1,3) ; ┌─id─┬─name─┬─sign─┬─version─┐ │ 1 │ a │ 1 │ 1 │ │ 1 │ b │ 1 │ 2 │ └────┴──────┴──────┴─────────┘ ┌─id─┬─name─┬─sign─┬─version─┐ │ 1 │ a2 │ 1 │ 3 │ └────┴──────┴──────┴─────────┘ --相同主键的数据可以添加,但是在合并时会删除 insert into testoVCMTree values(1,'a',-1,2) ; --对主键为1,版本号为2的数据进行折叠 ┌─id─┬─name─┬─sign─┬─version─┐ │ 1 │ a │ 1 │ 1 │ │ 1 │ a2 │ 1 │ 3 │ └────┴──────┴──────┴─────────┘
必须注意
- 在录入数据过程中,版本号不能相同(最好直接使用时间函数now()),如果版本号相同,会导致ClickHouse数据混乱
- 如果使用分区,那么在不同的分区中,即使主键相同(重复),数据也不会合并,而是同时存在
6.SummingMergeTree
- 该引擎继承自 MergeTree。
- 区别在于,当合并 SummingMergeTree 表的数据片段时,ClickHouse 会把所有具有相同主键的行合并为一行,该行包含了被合并的行中具有数值数据类型的列的汇总值。
- 如果主键的组合方式使得单个键值对应于大量的行,则可以显著的减少存储空间并加快数据查询的速度,对于不可加的列,会取一个最先出现的值(不好控制)。
使用演示:
-
建表/导入数据
create table testtosumtree( id Int8 , name String , ctime Date , cost UInt8 ) engine=SummingMergeTree(cost) order by id partition by name ; insert into testtosumtree values(1,'wangben','2020-12-06',1),(1,'shsh','2019-11-11',20) ; insert into testtosumtree values(1,'wangben','2020-12-05',1),(1,'shsh','2019-11-11',20) ; insert into testtosumtree values(1,'wangben','2020-12-03',1),(1,'shsh','2019-11-11',20) ; insert into testtosumtree values(1,'wangben','2020-12-04',1),(1,'shsh','2019-11-11',20) ;
-
合并数据
-
合并数据时,一次合并(optimize)语句,只会合并一个分区,如果有多个分区,需要多次执行合并语句;
-
除了主键和引擎关键字段的其他关键字,在合并时,理论上保留最先输入的数值。
-
四 特色引擎
1.集成引擎MySQL
1.1 MySQL的数据库引擎
可以创建一个连接MySQL中某个数据库的数据库引擎,直接对MySQL进行映射,形成连接
使用演示:
create database db_ch_mysql engine=MySQL('linux01:3306','doit','root','root');
可以直接使用MySQL数据中的表.
注意:该数据库只读,不能添加或修改
1.2 MySQL表引擎
- 将MySQL中的一个表,映射到ClickHouse中来
- 在ClickHouse本地文件中看不到这个表(因为只是一个映射)
- 可以添加数据
使用演示:
create table tb_user(
id Int32 ,
name String
)
engine=MySQL('linux01:3306','doit','tb_user','root','root') ;
2.集成引擎HDFS
-
和MySQL类似,连接hdfs中的某个文件,映射为ClickHouse中的表
-
本地表、MySQL表和hdfs的表都可以互相关联操作
-
TSV和CSV
两种结构化数据文件格式(文本类型):
TSV是以tab键分隔数据
CSV是以逗号分隔数据
使用演示:
create table tb_ch_hdfs(
uid String ,
name String ,
city String ,
age UInt8
)
engine = HDFS("hdfs://linux01:8020/ch/data/user.tsv" , "TSV") ;
--关联hdfs可以使用*关联多个文件,但是不能指定目录(必须要指定到文件)
2.1 cast函数
可以进行数据类型转换:
cast(val,'String')
cast(val as String)
3.File引擎
-
File表引擎以特殊的文件格式(TabSeparated,Native等)将数据保存在文件中。
-
用法示例: File(Format)
-
支持的文件格式:
https://clickhouse.tech/docs/v20.3/en/interfaces/formats/#formats
3.1 常规使用
- 建表
create table tb_file_demo1(uid UInt16 , name String) engine=File(TabSeparated) ;
- 在指定的目录下创建数据文件
/var/lib/ClickHouse/data/default/tb_file_demo1
[root@linux01 tb_file_demo1]# vi data.TabSeparated 注意这个文件的名字不能变化
1001 TaoGe
1002 XingGe
1003 HANGGE
- 查询数据
3.2 local语法
使用ClickHouse的方式读本地文件
echo -e "1,2\n3,4" | clickhouse-local -q "CREATE TABLE tb_local1 (a Int64, b Int64) ENGINE = File(CSV, stdin);select * from tb_local1;"
cat ./data.CSV | clickhouse-local -q "CREATE TABLE tb_local2 (id Int64, name String) ENGINE = File(CSV, stdin); select * from tb_local2 ;"
clickhouse-local -q
为固定写法- stdin是输入
- stdout是输出
- 形成的表是临时的、虚拟的,不会添加到ClickHouse数据库中;
- 可以通过编写脚本的方式,来处理一些小文件。
3.3 client数据导入
将本地文件数据导入到ClickHouse表中
操作步骤:
-
在CH中建表
create table tb_client (id UInt16 , name String) engine=TinyLog ;
-
将数据导入
cat data.CSV | ClickHouse-client --query="INSERT INTO tb_client FORMAT CSV"; cat data.CSV | ClickHouse-client -q "INSERT INTO tb_client FORMAT CSV"; --一样的 cat data.CSV | ClickHouse-client -q "INSERT INTO tb_client FORMAT CSV"
–format_csv_delimiter="," ; --指定分隔符
cat shop.csv | clickhouse-client -q "create table test_shop(name String,ctime Date ,money Float64) engine = File(CSV , 0 );"---失败,不能创建表格
```
* 注意`FORMAT CSV` 大写
**数据导入注意事项 : **
全量数据导入:数据导入临时表 -> 导入完成后,将原表改名为tmp1 -> 将临时表改名为正式表 -> 删除原表
增量数据导入: 增量数据导入临时表 -> 将原数据除增量外的也导入临时表 -> 导入完成后,将原表改名为tmp1-> 将临时表改成正式表-> 删除原数据表
五 常用操作
1.数据导入
1.1 通过Hive
-
使用hive建表,指定文件格式
-
hive的文件保存在hdfs中
-
通过集成引擎hdfs,直接创建表格,参考第四章第2节,这里不再演示
-
在加载过程中,注意数据类型的匹配
-
在导入过程中,注意hive和CH对于不同文件格式的命名方式
-
在hive中的表数据存储格式:
ORCFILE
Parquet
TestFile
-
在CH中
ORC
Parquet
CSV、TSV
-
1.2 client导入本地文件
参考第四章3.3节
2.使用from直接查询
2.1 File
使用from直接查询本地文件
SELECT * FROM file('user.csv', CSV, 'id Int8 , name String , age Int8 , gender String')
第一个字段—文件名
第二个字段—文件格式
第三个字段—表构造
- 文件必须在指定的路径下 /var/lib/clickhouse/user_files
- 可以通过修改clickhosue的配置文件的位置 (默认在/etc/clickouse-server下 )
2.2 HDFS
使用from查询hdfs中的文件
(文件可以是hive存储在hdfs中的,可以是本地的文件,或者其他hdfs中的符合CH格式的文件)
hdfs(URI, format, structure)
SELECT *
FROM hdfs(`hdfs://linux01:8020/user/hive/warehouse/db_doit19.db/tb_teacher2/000000_0`, ORC, 'name String , xz String ,xx String')
- 注意特殊的文件格式如ORC等
- 因为这些文件中本身就包含了字段名和数据类型等信息
- 在定义structure时,列名和数据类型要相对应
- 列名不能自定义,要和文件一致
- 可以不全取,自定义需要的字段,但是一定要解析有的字段
2.3 MySQL
SELECT * FROM mysql('linux01:3306', 'db_doit19', 'tb_user', 'root', 'root') ;
使用方法与前两种大致相同
3.炸裂和聚合
3.1 array join
- 与hive中的炸裂函数(explode + lateral view)使用方式相同
- 对指定数组进行分解,将分解后的字段和行中其他的字段分别进行重组,组合成新的多行数据。
使用演示:
--建表
create table test_arrjoin(
id Int8 ,
arr Array(String)
)engine=Memory ;
--添加数据
insert into test_arrjoin values(1,['a1','a2']),(2,array('b1','b2','b3')),(3,[]) ;
┌─id─┬─arr──────────────┐
│ 1 │ ['a1','a2'] │
│ 2 │ ['b1','b2','b3'] │
│ 3 │ [] │
└────┴──────────────────┘
--使用array join进行分解
select id ,arr
from test_arrjoin
array join arr ;
┌─id─┬─arr─┐
│ 1 │ a1 │
│ 1 │ a2 │
│ 2 │ b1 │
│ 2 │ b2 │
│ 2 │ b3 │
└────┴─────┘
--left array join(按其他字段进行补全)
-- 如果对arr重新命名了,那么arr就可以代表原来的数组;如果没有as命名,arr就是被炸裂的字段名
select id ,arr ,x1
fromtest_arrjoin
left array join arr as x1 ;
┌─id─┬─arr─┐
│ 1 │ a1 │
│ 1 │ a2 │
│ 2 │ b1 │
│ 2 │ b2 │
│ 2 │ b3 │
│ 3 │ │
└────┴─────┘
--array join后面也可使用自定义的数组
select id , arr ,x
from test_arrjoin
array join[1,2,3] as x;
┌─id─┬─arr──────────────┬─x─┐
│ 1 │ ['a1','a2'] │ 1 │
│ 1 │ ['a1','a2'] │ 2 │
│ 1 │ ['a1','a2'] │ 3 │
│ 2 │ ['b1','b2','b3'] │ 1 │
│ 2 │ ['b1','b2','b3'] │ 2 │
│ 2 │ ['b1','b2','b3'] │ 3 │
│ 3 │ [] │ 1 │
│ 3 │ [] │ 2 │
│ 3 │ [] │ 3 │
└────┴──────────────────┴───┘
3.2 groupArray()
- 与hive中的concat+collect使用大致相同
使用演示 :
-- 建表
create table test_grouparr(
id Int8 ,
name String
)engine = Log ;
-- 导入数据
insert into test_grouparr values(1,'a1'),(1,'a2'),(1,'a3'),(2,'b1'),(2,'b2'),(2,'b3') ;
┌─id─┬─name─┐
│ 1 │ a1 │
│ 1 │ a2 │
│ 1 │ a3 │
│ 2 │ b1 │
│ 2 │ b2 │
│ 2 │ b3 │
└────┴──────┘
-- 使用groupArray()聚合
select
id ,
groupArray(name) arr
from test_grouparr
group by id;
┌─id─┬─arr──────────────┐
│ 1 │ ['a1','a2','a3'] │
│ 2 │ ['b1','b2','b3'] │
└────┴──────────────────┘
-- arrayEnumerate(arr)函数:获取一个数组的全部索引,返回一个数组
select id , groupArray(name) arr , arrayEnumerate(arr) arr_index
from test_grouparr
group by id;
┌─id─┬─arr──────────────┬─arr_index─┐
│ 1 │ ['a1','a2','a3'] │ [1,2,3] │
│ 2 │ ['b1','b2','b3'] │ [1,2,3] │
└────┴──────────────────┴───────────┘
-- 可以通过array join 操作多个数组,前提是两个数组的长度要相同
select
id ,e ,i
from
(
select
id ,
groupArray(name) arr ,
arrayEnumerate(arr) arr2
--这里数组是怎么来的不影响,长度匹配就可以,
--[23,44,5555] arr2 结果相同
from
test_grouparr
group by id
)
array join
arr as e ,
arr2 as i ;
┌─id─┬─e──┬─i─┐
│ 1 │ a1 │ 1 │
│ 1 │ a2 │ 2 │
│ 1 │ a3 │ 3 │
│ 2 │ b1 │ 1 │
│ 2 │ b2 │ 2 │
│ 2 │ b3 │ 3 │
└────┴────┴───┘
4.常用函数
ClickHouse中的SQL使用和hive/mysql大致相同,一些特殊语法使用方式不同,在此进行介绍:
4.1 常用函数
- with as
-- 定义一个变量供select使用
with 'uid0001' as v
select * from tb_user where id = v ;
-- 查询一个结果供select使用(都是一个固定值)
with (select count(1) from tb_user) as cnt
select sum(age) / cnt from tb_user ;
-- 待验证
with tb_user as (select id , upper(name) from tb_user)
select * from tb_user ;
-
distinct 去重
-
FORMAT
-- 指定输出和输入的数据格式 clickhouse-client -q "select * from db_doit19.tb_user FORMAT XML"
-
输出的文件类型
file Creates a File-engine table.
merge Creates a Merge-engine table.
numbers Creates a table with a single column filled with integer numbers.
remote Allows you to access remote servers without creating a Distributed-engine table.
url Creates a Url-engine table.
mysql Creates a MySQL-engine table.
jdbc Creates a JDBC-engine table.
odbc Creates a ODBC-engine table.
hdfs Creates a HDFS-engine table.
-
-
limit by
-- 建表添加数据 create table tb_limit( id Int8 , name String , score Float64 )engine=Log ; insert into tb_limit values(1,'zss',77),(1,'zss',79),(1,'zss',99),(1,'zss',89) ; insert into tb_limit values(2,'lss',66),(2,'lss',69),(2,'lss',61),(2,'lss',69) ; ┌─id─┬─name─┬─score─┐ │ 2 │ lss │ 66 │ │ 2 │ lss │ 69 │ │ 2 │ lss │ 61 │ │ 2 │ lss │ 69 │ └────┴──────┴───────┘ ┌─id─┬─name─┬─score─┐ │ 1 │ zss │ 77 │ │ 1 │ zss │ 79 │ │ 1 │ zss │ 99 │ │ 1 │ zss │ 89 │ └────┴──────┴───────┘ -- 直接使用limit select * from tb_limit order by score desc limit 2; ┌─id─┬─name─┬─score─┐ │ 1 │ zss │ 99 │ │ 1 │ zss │ 89 │ └────┴──────┴───────┘ -- 使用limit by 可以通过by实现分组,分组之后再取组内前limit个 select * from tb_limit order by score desc limit 2 by name; ┌─id─┬─name─┬─score─┐ │ 1 │ zss │ 99 │ │ 1 │ zss │ 89 │ └────┴──────┴───────┘ ┌─id─┬─name─┬─score─┐ │ 2 │ lss │ 69 │ │ 2 │ lss │ 69 │ └────┴──────┴───────┘
4.2 集合相关函数
empty()
--输入数组或字符串
--判断是否为空
--返回UInt8,是空返回1,不空返回0
notEmpty()
--与empty()相反
length()
--输入数组或字符串
--返回长度(UInt16)
emptyArrayString
--返回一个空数组,所有类型都可以
range(end), range(start, end [, step])
--直接返回一个UInt类型数组
--输入开始值,结束值,跳跃数量(默认1)
--使用演示:
linux01 :) select range(1,20,2);
SELECT range(1, 20, 2)
┌─range(1, 20, 2)────────────┐
│ [1,3,5,7,9,11,13,15,17,19] │
└────────────────────────────┘
array(x1, …), operator [x1, …]
--数组的构造函数
--使用此方法只能构造有参数的数组,想要空数组使用emptyArrayString
arrayConcat(arrays)
--合并数组(连接数组)
SELECT arrayConcat([1, 2], [3, 4], [5, 6]) AS res
┌─res───────────┐
│ [1,2,3,4,5,6] │
└───────────────┘
arrayElement(arr,n)
--取出arr数组中,索引为n的值
--n输入-1时取最后一个数值
has(arr, elem)
--判断数组中是否包含参数elem
hasAll(set, subset)
--检查一个数组是否是另一个数组的子集
hasAny(array1, array2)
--检查两个数组是否有某些元素相交。
hasSubstr(array1, array2)
--检查1数组是否包含2数组,且顺序完全相同
arrayCount([func,] arr1,…)
--高级函数:函数的参数,是其他函数或方法的函数
--输入:lambda表达式,数组
--返回数组中符合lambda表达式的元素的个数
SELECT arrayCount(x -> ((x + 10) > 100), [10, 20, 50, 90, 100, 110])
┌─arrayCount(lambda(tuple(x), greater(plus(x, 10), 100)), [10, 20, 50, 90, 100, 110])─┐
│ 2 │
└─────────────────────────────────────────────────────────────────────────────────────┘
4.3 Json
visitParamHas(params, name)
--params:Json格式的行数据
--检查该行Json中是否存在内容为name的键
Geohash
5.分区表
与hive分区方法完全相同
create table tb_p2(
oid String ,
money Float64 ,
cDate Date
) engine = MergeTree
order by oid
partition by toMonth(cDate) ;
-- 月进行分区
insert into tb_p2 values ('002',99,'2020-12-01') ,('001',98,'2020-12-01') ,('003',199,'2020-12-02'),('004',299,'2020-11-02');
┌─oid─┬─money─┬──────cDate─┐
│ 001 │ 98 │ 2020-12-01 │
│ 002 │ 99 │ 2020-12-01 │
│ 003 │ 199 │ 2020-12-02 │
└─────┴───────┴────────────┘
┌─oid─┬─money─┬──────cDate─┐
│ 004 │ 299 │ 2020-11-02 │
└─────┴───────┴────────────┘
-- select数据与hive略有不同,不同分区会分开显示
6.漏斗函数
windowFunnel(间隔时间)(toDateTime(时间字段),eventid = '事件1',eventid = '事件2',...) as funnel
使用说明 :
- 在规定的间隔时间内,从事件1的时间字段起,连续执行到事件n,事件n的时间字段-事件1的时间字段要小于等于设置的额时间间隔,返回—n
使用演示 :
建表/导入数据
CREATE TABLE test_funnel ( uid String, eventid String, eventTime UInt64) ENGINE = MergeTree PARTITION BY (uid, eventTime) ORDER BY (uid, eventTime);
cat funnel.csv | clickhouse-client -q "INSERT INTO test_funnel FORMAT CSV";
SELECT *
FROM test_funnel
ORDER BY (uid, eventTime) ASC
┌─uid──┬─eventid─┬──eventTime─┐
│ uid1 │ event1 │ 1551398100 │
│ uid1 │ event2 │ 1551398180 │
│ uid1 │ event3 │ 1551398260 │
│ uid1 │ event4 │ 1551398340 │
│ uid1 │ event1 │ 1551398500 │
│ uid1 │ event2 │ 1551398540 │
│ uid1 │ event3 │ 1551398580 │
│ uid1 │ event1 │ 1551398700 │
│ uid1 │ event2 │ 1551398705 │
│ uid2 │ event2 │ 1551398412 │
│ uid2 │ event3 │ 1551398415 │
│ uid3 │ event3 │ 1551398410 │
│ uid3 │ event4 │ 1551398413 │
└──────┴─────────┴────────────┘
使用漏斗函数查看事件
select
uid,
windowFunnel(100)
(toDateTime(eventTime),eventid = 'event1',eventid = 'event2',eventid = 'event3',eventid = 'event4') as funnel
from test_funnel
group by uid
order by uid;
┌─uid──┬─funnel─┐
│ uid1 │ 3 │
│ uid2 │ 0 │
│ uid3 │ 0 │
└──────┴────────┘
select uid,
windowFunnel(240)
(toDateTime(eventTime),eventid = 'event1',eventid = 'event2',eventid = 'event3',eventid = 'event4') as funnel
from test_funnel
group by uid
order by uid;
┌─uid──┬─funnel─┐
│ uid1 │ 4 │
│ uid2 │ 0 │
│ uid3 │ 0 │
└──────┴────────┘
select
uid,
windowFunnel(239)
(toDateTime(eventTime),eventid = 'event1',eventid = 'event2',eventid = 'event3',eventid = 'event4') as funnel
from test_funnel
group by uid
order by uid;
┌─uid──┬─funnel─┐
│ uid1 │ 3 │
│ uid2 │ 0 │
│ uid3 │ 0 │
└──────┴────────┘