一 数据库操作
Hive数据库操作,与MySql有很多都是一致的
创建数据库
create database if not exists myhive;
use myhive;
查看数据库详细信息
desc database myhive;
数据库本质上就是在HDFS之上的文件夹,是一个以.db结尾的目录,默认存储在:/user/hive/warehouse内,可以通过LOCATION关键字在创建的时候指定存储目录。
创建数据库并指定hdfs存储位置
create database myhive2 location '/myhive2';
使用location关键字,可以指定数据库在HDFS的存储路径。
删除一个空数据库,如果数据库下面有数据表,那么就会报错
drop database myhive;
强制删除数据库,包含数据库下面的表一起删除
drop database myhive2 cascade;
二 表操作
2.1 基本语法
创建数据库表语法
创建表的语法还是比较复杂的
- EXTERNAL,创建外部表
- PARTITIONED BY, 分区表
- CLUSTERED BY,分桶表
- STORED AS,存储格式
- LOCATION,存储位置
数据类型
Hive中支持的数据类型还是比较多的,其中红色的是使用比较多的类型。
基础创建表示意
尽管建表语法比较复杂,目前我们暂时未接触到分区、分桶等概念。所以,创建一个简单的数据库表可以有如下SQL:
CREATE TABLE test(id INT,name STRING,gender STRING
);
删除表
如果要删除表可以使用:
DROP TABLE table_name;
2.2 表分类
Hive中可以创建的表有好几种类型, 分别是:
- 内部表
- 外部表
- 分区表
- 分桶表
不同类型的表有各自的用途。我们首先学习内部表和外部表的区别。
内部表和外部表
内部表(CREATE TABLE table_name …)
未被external关键字修饰的表就是内部表, 即普通表。 内部表又称管理表,内部表数据存储的位置由hive.metastore.warehouse.dir参数决定(默认:/user/hive/warehouse),删除内部表会直接删除元数据(metadata)及存储数据,因此内部表不适合和其他工具共享数据。
外部表(CREATE EXTERNAL TABLE table_name …LOCATION…)
被external关键字修饰的就是外部表, 即关联表。外部表是指表数据可以在任何位置,通过LOCATION关键字指定。 数据存储的不同也代表了这个表在理念上并不是Hive内部管理的,而是可以随意临时链接到外部数据上的。
所以,在删除外部表的时候, 仅仅是删除元数据(表的信息),不会删除数据本身。
快速对比一下内部表和外部表
2.2.1 内部表
创建内部表
内部表的创建语法就是标准的:CREATE TABLE table_name......
创建一个基础的表:
create database if not exists myhive;
use myhive;
create table if not exists stu(id int,name string);
insert into stu values (1,"zhangsan"), (2, "wangwu");
select * from stu;
查看表的数据存储
在HDFS上,查看表的数据存储文件
可以看到,数据在HDFS上也是以明文文件存在的。奇怪的是, 列ID和列NAME,好像没有分隔符,而是挤在一起的。这是因为,默认的数据分隔符是:”\001”是一种特殊字符,是ASCII值,键盘是打不出来的,在某些文本编辑器中是显示为SOH的。
当然,分隔符我们是可以自行指定的。在创建表的时候可以自己决定:
create table if not exists stu2(id int ,name string) row format delimited fields terminated by '\t';
- row format delimited fields terminated by ‘\t’:表示以\t分隔
其它创建内部表的形式
除了标准的CREATE TABLE table_name的形式创建内部表外,我们还可以通过:
- CREATE TABLE table_name as,基于查询结果建表
create table stu3 as select * from stu2;
- CREATE TABLE table_name like,基于已存在的表结构建表
create table stu4 like stu2;
- 也可以使用DESC FORMATTED table_name,查看表类型和详情
DESC FORMATTED stu2;
删除内部表
我们是内部表,删除后,数据本身也不会保留,让我们试一试吧。
DROP TABLE table_name,删除表
drop table stu2;
可以看到,stu2文件夹已经不存在了,数据被删除了。
2.2.2 外部表
外部表的创建
外部表,创建表被EXTERNAL关键字修饰,从概念是被认为并非Hive拥有的表,只是临时关联数据去使用。
创建外部表也很简单,基于外部表的特性,可以总结出: 外部表 和 数据 是相互独立的, 即:
- 可以先有表,然后把数据移动到表指定的LOCATION中
- 也可以先有数据,然后创建表通过LOCATION指向数据
-
在Linux上创建新文件,test_external.txt,并填入如下内容:
数据列用’\t’分隔 -
演示先创建外部表,然后移动数据到LOCATION目录
- 首先检查:
hadoop fs -ls /tmp
,确认不存在/tmp/test_ext1目录 - 创建外部表:
create external table test_ext1(id int, name string) row format delimited fields terminated by ‘\t’ location ‘/tmp/test_ext1’;
(注意:必须使用row format delimited fields terminated by指定列分隔符,必须使用LOCATION指定数据路径) - 可以看到,目录/tmp/test_ext1被创建
select * from test_ext1
,空结果,无数据- 上传数据:
hadoop fs -put test_external.txt /tmp/test_ext1/
select * from test_ext1
,即可看到数据结果
- 首先检查:
-
演示先存在数据,后创建外部表
- hadoop fs -mkdir /tmp/test_ext2
- hadoop fs -put test_external.txt /tmp/test_ext2/
- create external table test_ext2(id int, name string) row format delimited fields terminated by ‘\t’ location ‘/tmp/test_ext2’;
- select * from test_ext2;
删除外部表
drop table test_ext1;
drop table test_ext2;
可以发现,在Hive中通过show table,表不存在了。但是在HDFS中,数据文件依旧保留
2.2.3 内外部表转换
Hive可以很简单的通过SQL语句转换内外部表。查看表类型:desc formatted stu;
内部表转外部表
alter table stu set tblproperties('EXTERNAL'='TRUE');
外部表转内部表
alter table stu set tblproperties('EXTERNAL'='FALSE');
通过stu set tblproperties
来修改属性,要注意:('EXTERNAL'='FALSE')
或 ('EXTERNAL'='TRUE')
为固定写法,区分大小写!!!
三 表数据
3.1 数据加载(数据导入)
方式一 LOAD语法
我们使用 LOAD 语法,把外部将数据加载到Hive内,语法如下:
案例
建表如下:
CREATE TABLE myhive.test_load(dt string comment '时间(时分秒)', user_id string comment '用户ID', word string comment '搜索词',url string comment '用户访问网址'
) comment '搜索引擎日志表' ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t';
数据如下:
数据加载
load data local inpath '/home/hadoop/search_log.txt' into table myhive.test_load;
# 不使用location 说明数据在hdfs
load data inpath '/tmp/search_log.txt' overwrite into table myhive.test_load;
注意,基于HDFS进行load加载数据,源数据文件会消失(本质是被移动到表所在的目录中)
方式二 INSERT SELECT 语法
除了load加载外部数据外,我们也可以通过SQL语句,从其它表中加载数据。
语法:
INSERT [OVERWRITE | INTO] TABLE tablename1 [PARTITION (partcol1=val1, partcol2=val2 ...) [IF NOT EXISTS]] select_statement1 FROM from_statement;
将SELECT查询语句的结果插入到其它表中,被SELECT查询的表可以是内部表或外部表。
示例:
INSERT INTO TABLE tbl1 SELECT * FROM tbl2;
INSERT OVERWRITE TABLE tbl1 SELECT * FROM tbl2;
对于数据加载,我们学习了:LOAD和INSERT SELECT的方式,那么如何选择它们使用呢?
- 数据在本地:推荐 load data local加载
- 数据在HDFS:如果不保留原始文件,推荐使用LOAD方式直接加载。如果保留原始文件,推荐使用外部表先关联数据,然后通过INSERT SELECT 外部表的形式加载数据
- 数据已经在表中:只可以INSERT SELECT
推荐选择加载数据到内部表内
- 部表就像企业的外聘顾问一样,并非正式成员,在设计上只是临时工,一般用于中转数据或临时使用。
- 外部表存储位置不固定,权限管控不统一,容易出现数据丢失问题。
所以,我们需要学习数据加载,完成将外部数据导入到Hive内部表中
3.2 数据导出
方式一 insert overwrite
将hive表中的数据导出到其他任意目录,例如linux本地磁盘,例如hdfs,例如mysql等等
insert overwrite [local] directory ‘path’ select_statement1 FROM from_statement;
将查询的结果导出到本地 - 使用默认列分隔符
insert overwrite local directory '/home/hadoop/export1' select * from test_load ;
将查询的结果导出到本地 - 指定列分隔符
insert overwrite local directory '/home/hadoop/export2' row format delimited fields terminated by '\t' select * from test_load;
将查询的结果导出到HDFS上(不带local关键字)
insert overwrite directory '/tmp/export' row format delimited fields terminated by '\t' select * from test_load;
方式二 hive shell
基本语法:(hive -f/-e 执行语句或者脚本 > file)
bin/hive -e "select * from myhive.test_load;" > /home/hadoop/export3/export4.txtbin/hive -f export.sql > /home/hadoop/export4/export4.txt
四 Hive复杂类型的基本使用
Hive支持的数据类型很多,除了基本的:int、string、varchar、timestamp等,还有一些复杂的数据类型:
array:数组类型
map:映射类型
struct:结构类型
4.1 array类型
如下是 data_for_array_type.txt 数据文件,有2个列,locations列包含多个城市:
说明:name与locations之间制表符分隔,locations中元素之间逗号分隔
可以使用array数组类型,存储locations的数据,建表语句:
create table myhive.test_array(name string, work_locations array<string>)
row format delimited fields terminated by '\t'
COLLECTION ITEMS TERMINATED BY ',';
其中:
row format delimited fields terminated by ‘\t’ 表示列分隔符是\t
COLLECTION ITEMS TERMINATED BY ‘,’ 表示集合(array)元素的分隔符是逗号
导入数据
load data local inpath '/home/hadoop/data_for_array_type.txt' overwrite into table myhive.test_array;
常用array类型查询:
-- 查询所有数据
select * from myhive.test_array;
-- 查询loction数组中第一个元素
select name, work_locations[0] location from myhive.test_array;
-- 查询location数组中元素的个数
select name, size(work_locations) location from myhive.test_array;
-- 查询location数组中包含tianjin的信息
select * from myhive.test_array where array_contains(work_locations,'tianjin');
4.2 map类型
map类型其实就是简单的指代:Key-Value型数据格式。 有如下数据文件,其中
- members字段是key-value型数据
- 字段与字段分隔符: “,”
- 需要map字段之间的分隔符:“#”
- map内部k-v分隔符:“:”
id,name,members,age
1,zhangsan,father:xiaoming#mother:xiaohuang#brother:xiaoxu,28
2,lisi,father:mayun#mother:huangyi#brother:guanyu,22
3,wangwu,father:wangjianlin#mother:ruhua#sister:jingtian,29
4,mayun,father:mayongzhen#mother:angelababy,26
建表语句:
create table myhive.test_map(
id int, name string, members map<string,string>, age int
)
row format delimited
fields terminated by ','
COLLECTION ITEMS TERMINATED BY '#'
MAP KEYS TERMINATED BY ':';
其中:
MAP KEYS TERMINATED BY ‘:’ 表示key-value之间用:分隔
导入数据
load data local inpath '/home/hadoop/data_for_map_type.txt' overwrite into table myhive.test_map;
常用查询
# 查询全部
select * from myhive.test_map;
# 查询father、mother这两个map的key
select id, name, members['father'] father, members['mother'] mother, age from myhive.test_map;
# 查询全部map的key,使用map_keys函数,结果是array类型
select id, name, map_keys(members) as relation from myhive.test_map;
# 查询全部map的value,使用map_values函数,结果是array类型
select id, name, map_values(members) as relation from myhive.test_map;
# 查询map类型的KV对数量
select id,name,size(members) num from myhive.test_map;
# 查询map的key中有brother的数据
select * from myhive.test_map where array_contains(map_keys(members), 'brother');
4.3 struct类型
struct类型是一个复合类型,可以在一个列中存入多个子列,每个子列允许设置类型和名称
有如下数据文件,说明:字段之间#分割,struct之间冒号分割
1#周杰轮:11
2#林均杰:16
3#刘德滑:21
4#张学油:26
5#蔡依临:23
建表语句
create table myhive.test_struct(id string, info struct<name:string, age:int>
)
row format delimited
fields terminated by '#'
COLLECTION ITEMS TERMINATED BY ':';
导入数据
load data local inpath '/home/hadoop/data_for_struct_type.txt' into table myhive.test_struct;
常用查询
select * from hive_struct;
# 直接使用列名.子列名 即可从struct中取出子列查询
select ip, info.name from hive_struct;
五 分区表
在大数据中,最常用的一种思想就是分治,我们可以把大的文件切割划分成一个个的小的文件,这样每次操作一个小的文件就会很容易了。
同样的道理,在hive当中也是支持这种思想的,就是我们可以把大的数据,按照每天,或者每小时进行切分成一个个的小的文件,这样去操作小的文件就会容易得多了。
如图,一个典型的按月份分区的表。每一个分区,是一个文件夹。
同时Hive也支持多个字段作为分区,多分区带有层级关系,如下图,多分区表(三级分区)
分区其实就是HDFS上的不同文件夹。分区表可以极大的提高特定场景下Hive的操作性能。
分区表的使用
基本语法:
create table tablename(...) partitioned by (分区列 列类型, ......)
row format delimited fields terminated by '';
六 分桶表
分桶和分区一样,也是一种通过改变表的存储模式,从而完成对表优化的一种调优方式。
但和分区不同,分区是将表拆分到不同的子文件夹中进行存储,而分桶是将表拆分到固定数量的不同文件中进行存储。
分桶表创建
开启分桶的自动优化(自动匹配reduce task数量和桶数量一致)
set hive.enforce.bucketing=true;
创建分桶表
# clustered by指定分桶字段
# into num buckets指定分桶数量
create table course (c_id string,c_name string,t_id string) clustered by(c_id) into 3 buckets row format delimited fields terminated by '\t';
分桶表数据加载
桶表的数据加载,由于桶表的数据加载通过load data无法执行,只能通过insert select
,所以,比较好的方式是
- 创建一个临时表(外部表或内部表均可),通过
load data
加载数据进入表 - 然后通过
insert select
从临时表向桶表插入数据
为什么不可以用load data,必须用insert select插入数据?
需要insert select触发MapReduce进行hash取模计算,来基于分桶列的值,确定哪一条数据进入到哪一个桶文件中
如果没有分桶设置,插入(加载)数据只是简单的将数据放入到:
- 表的数据存储文件夹中(没有分区)
- 表指定分区的文件夹中(带有分区)
一旦有了分桶设置,比如分桶数量为3,那么,表内文件或分区内数据文件的数量就限定为3,当数据插入的时候,需要一分为3,进入三个桶文件内。
问题就在于:如何将数据分成三份,划分的规则是什么?
数据的三份划分基于分桶列的值进行hash取模来决定
由于load data不会触发MapReduce,也就是没有计算过程(无法执行Hash算法),只是简单的移动数据而已,所以无法用于分桶表数据插入。
Hash取模
Hash算法是一种数据加密算法,其原理我们不去详细讨论,我们只需要知道其主要特征:
- 同样的值被Hash加密后的结果是一致的
比如字符串“hadoop”被Hash后的结果是12345(仅作为示意),那么无论计算多少次,字符串“hadoop”的结果都会是12345。
比如字符串“bigdata”被Hash后的结果是56789(仅作为示意),那么无论计算多少次,字符串“bigdata”的结果都会是56789。
基于如上特征,再辅以有3个分桶文件的基础上,将Hash的结果基于3取模(除以3 取余数),那么,可以得到如下结果:
- 无论什么数据,得到的取模结果均是:0、1、2 其中一个
- 同样的数据得到的结果一致,如hadoop hash取模结果是1,无论计算多少次,字符串hadoop的取模结果都是1
所以,必须使用insert select的语法,因为会触发MapReduce,进行hash取模计算。
Hash取模确定数据归属哪个分桶文件
基于Hash取模,数据中的每一个分桶列的值,都被hash取模得到0、1、2其中一个数,基于结果,存入对应序号的桶文件中。
分桶表的性能提升
如果说分区表的性能提升是:在指定分区列的前提下,减少被操作的数据量,从而提升性能。
分桶表的性能提升就是:基于分桶列的特定操作,如:过滤、JOIN、分组,均可带来性能提升。
基于分桶列,过滤单个值
基于分桶列,进行双表JOIN
基于分桶列,group by 分组
七 修改表
表重命名
alter table old_table_name rename to new_table_name;
如:alter table score4 rename to score5;
修改表属性值
ALTER TABLE table_name SET TBLPROPERTIES table_properties;
table_properties:: (property_name = property_value, property_name = property_value, ... )
如:ALTER TABLE table_name SET TBLPROPERTIES("EXTERNAL"="TRUE"); 修改内外部表属性
如:ALTER TABLE table_name SET TBLPROPERTIES ('comment' = new_comment); 修改表注释
其余属性可参见:https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL#LanguageManualDDL-listTableProperties
添加分区
ALTER TABLE tablename ADD PARTITION (month='201101');
新分区是空的没数据,需要手动添加或上传数据文件
修改分区值
ALTER TABLE tablename PARTITION (month='202005') RENAME TO PARTITION (month='201105');
删除分区
ALTER TABLE tablename DROP PARTITION (month='201105');
添加列
ALTER TABLE table_name ADD COLUMNS (v1 int, v2 string);
修改列名
ALTER TABLE test_change CHANGE v1 v1new INT;
删除表
DROP TABLE tablename;
清空表
TRUNCATE TABLE tablename;
ps:只可以清空内部表
八 复杂类型操作
Hive支持的数据类型很多,除了基本的:int、string、varchar、timestamp等,还有一些复杂的数据类型:
- array
数组类型 - map
映射类型 - struct
结构类型
8.1 array类型
如下数据文件,有2个列,locations列包含多个城市:
说明:name与locations之间制表符分隔,locations中元素之间逗号分隔
可以使用array数组类型,存储locations的数据,建表语句:
create table myhive.test_array(name string, work_locations array<string>)
row format delimited fields terminated by '\t'
COLLECTION ITEMS TERMINATED BY ',';
row format delimited fields terminated by '\t'
表示列分隔符是\t
COLLECTION ITEMS TERMINATED BY ','
表示集合(array)元素的分隔符是逗号
基于COLLECTION ITEMS TERMINATED BY ','
设定的array类型的一条数据示意
导入数据
load data local inpath '/home/hadoop/data_for_array_type.txt' overwrite into table myhive.test_array;
常用array类型查询:
-- 查询所有数据
select * from myhive.test_array;
-- 查询loction数组中第一个元素
select name, work_locations[0] location from myhive.test_array;
-- 查询location数组中元素的个数
select name, size(work_locations) location from myhive.test_array;
-- 查询location数组中包含tianjin的信息
select * from myhive.test_array where array_contains(work_locations,'tianjin');
8.2 map类型
map类型其实就是简单的指代:Key-Value型数据格式。 有如下数据文件,其中members字段是key-value型数据,字段与字段分隔符: “,”;需要map字段之间的分隔符:“#”;map内部k-v分隔符:“:”
id,name,members,age
1,zhangsan,father:xiaoming#mother:xiaohuang#brother:xiaoxu,28
2,lisi,father:mayun#mother:huangyi#brother:guanyu,22
3,wangwu,father:wangjianlin#mother:ruhua#sister:jingtian,29
4,mayun,father:mayongzhen#mother:angelababy,26
建表语句
create table myhive.test_map(id int, name string, members map<string,string>, age int
)
row format delimited
fields terminated by ','
COLLECTION ITEMS TERMINATED BY '#'
MAP KEYS TERMINATED BY ':';
MAP KEYS TERMINATED BY ‘:’ 表示key-value之间用:分隔;
不同键值对之间:COLLECTION ITEMS TERMINATED BY ‘分隔符’ 分隔
基于map定义的结构一条数据示例
导入数据
load data local inpath '/home/hadoop/data_for_map_type.txt' overwrite into table myhive.test_map;
常用查询
# 查询全部
select * from myhive.test_map;
# 查询father、mother这两个map的key
select id, name, members['father'] father, members['mother'] mother, age from myhive.test_map;
# 查询全部map的key,使用map_keys函数,结果是array类型
select id, name, map_keys(members) as relation from myhive.test_map;
# 查询全部map的value,使用map_values函数,结果是array类型
select id, name, map_values(members) as relation from myhive.test_map;
# 查询map类型的KV对数量
select id,name,size(members) num from myhive.test_map;
# 查询map的key中有brother的数据
select * from myhive.test_map where array_contains(map_keys(members), 'brother');
- map[key]来获取指定key的值
- map_keys(map)取到全部的key作为array返回,map_values(map)取到全部values
- size(map)可以统计K-V对的个数
- array_contains(map_values(map), 数据) 可以统计map是否包含指定数据
8.3 struct类型
struct类型是一个复合类型,可以在一个列中存入多个子列,每个子列允许设置类型和名称
有如下数据文件,说明:字段之间#分割,struct之间冒号分割
1#周杰轮:11
2#林均杰:16
3#刘德滑:21
4#张学油:26
5#蔡依临:23
建表语句
create table myhive.test_struct(
id string, info struct<name:string, age:int>
)
row format delimited
fields terminated by '#'
COLLECTION ITEMS TERMINATED BY ':';
导入数据
load data local inpath '/home/hadoop/data_for_struct_type.txt' into table myhive.test_struct;
常用查询
select * from hive_struct;
# 直接使用列名.子列名 即可从struct中取出子列查询
select ip, info.name from hive_struct;
struct的分隔符只需要:COLLECTION ITEMS TERMINATED BY '分隔符'
只需要分隔数据即可(数据中不记录key,key是建表定义的固定的)
8.4 array、map、struct总结
九 数据查询
9.1 基本查询
查询语句的基本语法
SELECT [ALL | DISTINCT]select_expr, select_expr, ...
FROM table_reference
[WHERE where_condition]
[GROUP BYcol_list]
[HAVING where_condition]
[ORDER BYcol_list]
[CLUSTER BYcol_list| [DISTRIBUTE BY col_list] [SORT BY col_list]
]
[LIMIT number]
整体上和普通SQL差不多,部分有区别,如:CLUSTER BY、DISTRIBUTE BY、SORT BY等,这些我们会放入到后边高阶原理章节进行讲解。
准备数据:订单表
CREATE DATABASE itheima;USE itheima;CREATE TABLE itheima.orders (orderId bigint COMMENT '订单id',orderNo string COMMENT '订单编号',shopId bigint COMMENT '门店id',userId bigint COMMENT '用户id',orderStatus tinyint COMMENT '订单状态 -3:用户拒收 -2:未付款的订单 -1:用户取消 0:待发货 1:配送中 2:用户确认收货',goodsMoney double COMMENT '商品金额',deliverMoney double COMMENT '运费',totalMoney double COMMENT '订单金额(包括运费)',realTotalMoney double COMMENT '实际订单金额(折扣后金额)',payType tinyint COMMENT '支付方式,0:未知;1:支付宝,2:微信;3、现金;4、其他',isPay tinyint COMMENT '是否支付 0:未支付 1:已支付',userName string COMMENT '收件人姓名',userAddress string COMMENT '收件人地址',userPhone string COMMENT '收件人电话',createTime timestamp COMMENT '下单时间',payTime timestamp COMMENT '支付时间',totalPayFee int COMMENT '总支付金额'
) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t';LOAD DATA LOCAL INPATH '/home/hadoop/itheima_orders.txt' INTO TABLE itheima.orders;
这是一张订单销售表,我们基于此表,做一下简单的Hive基本查询
准备数据:用户表
CREATE TABLE itheima.users (userId int,loginName string,loginSecret int,loginPwd string,userSex tinyint,userName string,trueName string,brithday date,userPhoto string,userQQ string,userPhone string,userScore int,userTotalScore int,userFrom tinyint,userMoney double,lockMoney double,createTime timestamp,payPwd string,rechargeMoney double
) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t';
加载数据
LOAD DATA LOCAL INPATH '/home/hadoop/itheima_users.txt' INTO TABLE itheima.users;
单表查询
查询所有
SELECT * FROM itheima.orders;
查询单列
SELECT orderid, totalmoney, username, useraddress, paytime FROM itheima.orders;
查询数据量
SELECT COUNT(*) FROM itheima.orders;
过滤广东省订单
SELECT * FROM itheima.orders WHERE useraddress LIKE '%广东%';
找出广东省单笔营业额最大的订单
SELECT * FROM itheima.orders WHERE useraddress like '%广东%' ORDER BY totalmoney DESC LIMIT 1;
分组、聚合
统计未支付、已支付各自的人数
SELECT ispay, COUNT(*) AS cnt FROM itheima.orders GROUP BY ispay;
在已付款订单中,统计每个用户最高的一笔消费金额
SELECT userid, MAX(totalmoney) AS max_money FROM itheima.orders WHERE ispay = 1 GROUP BY userid;
统计每个用户的平均订单消费额
SELECT userid, AVG(totalmoney) FROM itheima.orders GROUP BY userid;
统计每个用户的平均订单消费额,过滤大于10000的数据
SELECT userid, AVG(totalmoney) AS avg_money FROM itheima.orders GROUP BY userid HAVING avg_money > 10000;
JOIN
JOIN订单表和用户表,找出用户名
SELECT o.orderid, o.userid, u.username, o.totalmoney, o.useraddress, o.paytime FROM itheima.orders o JOIN itheima.users u ON o.userid = u.userid;
左外关联,订单表和用户表,找出用户名
SELECT o.orderid, o.userid, u.username, o.totalmoney, o.useraddress, o.paytime FROM itheima.orders o LEFT JOIN itheima.users u ON o.userid = u.userid;
可以发现,Hive中使用基本查询SELECT、WHERE、GROUP BY、聚合函数、HAVING、JOIN和普通的SQL语句没有区别
9.2 RLIKE关键字进行正则匹配
正则表达式
正则表达式是一种规则集合,通过特定的规则字符描述,来判断字符串是否符合规则。
RLIKE
Hive中提供RLIKE关键字,可以供用户使用正则和数据进行匹配。我们以上一节中使用的订单表为例,来简单使用一下RLIKE正则匹配。
# 查找广东省的数据
SELECT * FROM itheima.orders WHERE useraddress RLIKE '.*广东.*';
# 查找用户地址是:xx省 xx市 xx区的数据
SELECT * FROM itheima.orders WHERE useraddress RLIKE '..省 ..市 ..区';
# 查找用户姓为张、王、邓
SELECT * FROM itheima.orders WHERE username RLIKE '[张王邓]\\S+';
# 查找手机号符合:188****0*** 规则
SELECT * FROM itheima.orders WHERE userphone RLIKEE '188\\S{4}0\\S{3}';
9.3 UNION关键字进行查询
UNION 用于将多个 SELECT 语句的结果组合成单个结果集。每个 select 语句返回的列的数量和名称必须相同。否则,将引发架构错误。
基础语法:
SELECT ...UNION [ALL]
SELECT ...
准备数据进行测试
CREATE TABLE itheima.course(c_id string, c_name string, t_id string)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t';LOAD DATA LOCAL INPATH '/home/hadoop/course.txt' INTO TABLE itheima.course;
联合两个查询结果集
SELECT * FROM course WHERE t_id = '周杰轮'UNION
SELECT * FROM course WHERE t_id = '王力鸿'
UNION联合 - 去重
UNION默认有去重功能:
直接联合两个同样的查询结果
SELECT * FROM course
UNION
SELECT * FROM course
如果不需要去重效果
SELECT * FROM courseUNION ALL
SELECT * FROM course
UNION写在FROM中
SELECT t_id, COUNT(*) FROM
(SELECT t_id FROM itheima.course WHERE t_id = '周杰轮'UNION ALLSELECT t_id FROM itheima.course WHERE t_id = '王力鸿'
) AS u GROUP BY t_id;
用于INSERT SELECT中
CREATE TABLE itheima.course2 LIKE itheima.course;INSERT OVERWRITE TABLE itheima.course2SELECT * FROM itheima.courseUNION ALLSELECT * FROM itheima.course;
9.4 Sampling采样
为什么需要抽样表数据?
对表进行随机抽样是非常有必要的。大数据体系下,在真正的企业环境中,很容易出现很大的表,比如体积达到TB级别。对这种表一个简单的SELECT * 都会非常的慢,哪怕LIMIT 10想要看10条数据,也会走MapReduce流程,这个时间等待是不合适的。
Hive提供的快速抽样的语法,可以快速从大表中随机抽取一些数据供用户查看。
TABLESAMPLE函数
进行随机抽样,本质上就是用TABLESAMPLE函数
语法1,基于随机分桶抽样:
SELECT ... FROM tbl TABLESAMPLE(BUCKET x OUT OF y ON(colname | rand()))
- y表示将表数据随机划分成y份(y个桶)
- x表示从y里面随机抽取x份数据作为取样
- colname表示随机的依据基于某个列的值
- rand()表示随机的依据基于整行
示例:
SELECT username, orderId, totalmoney FROM itheima.orders TABLESAMPLE(BUCKET 1 OUT OF 10 ON username);
SELECT * FROM itheima.orders TABLESAMPLE(BUCKET 1 OUT OF 10 ON rand());
注意:
- 使用colname作为随机依据,则其它条件不变下,每次抽样结果一致
- 使用rand()作为随机依据,每次抽样结果都不同
语法2,基于数据块抽样
SELECT ... FROM tbl TABLESAMPLE(num ROWS | num PERCENT | num(K|M|G));
- num ROWS 表示抽样num条数据
- num PERCENT 表示抽样num百分百比例的数据
- num(K|M|G) 表示抽取num大小的数据,单位可以是K、M、G表示KB、MB、GB
注意:使用这种语法抽样,条件不变的话,每一次抽样的结果都一致。 即无法做到随机,只是按照数据顺序从前向后取。
9.5 Virtual Columns 虚拟列
虚拟列是Hive内置的可以在查询语句中使用的特殊标记,可以查询数据本身的详细参数。
Hive目前可用3个虚拟列:
- INPUT__FILE__NAME,显示数据行所在的具体文件
- BLOCK__OFFSET__INSIDE__FILE,显示数据行所在文件的偏移量
- ROW__OFFSET__INSIDE__BLOCK,显示数据所在HDFS块的偏移量
- 此虚拟列需要设置:SET hive.exec.rowoffset=true 才可使用
示例:
SELECT *, INPUT__FILE__NAME, BLOCK__OFFSET__INSIDE__FILE, ROW__OFFSET__INSIDE__BLOCK FROM itheima.course;
虚拟列的作用
使用虚拟列,可以让我们更精准的查看到具体每一条数据在存储上的详细参数细节
虚拟列不仅仅可以用于SELECT,在WHERE、GROUP BY等均可使用,如:
SELECT *, BLOCK__OFFSET__INSIDE__FILE FROM course WHERE BLOCK__OFFSET__INSIDE__FILE > 50;
SELECT INPUT__FILE__NAME, COUNT(*) FROM itheima.orders_bucket GROUP BY INPUT__FILE__NAME;
如上SQL,统计分桶表每个桶的数据行数。
除此以外,在某些错误排查场景上,虚拟列可以提供相关帮助。
十 函数
分类标准
Hive的函数分为两大类:内置函数(Built-in Functions)、用户定义函数UDF(User-Defined Functions):
Hive的函数共计有上百种,这里无法一一讲解,会挑选一些常用的进行讲解。
详细的函数使用可以参阅:官方文档
常用函数如下
-- 查看所有可用函数
show functions;
-- 查看函数使用方式
describe function extended count;
-- 数值函数
-- round 取整,设置小数精度
select round(3.1415926); -- 取整(四舍五入)
select round(3.1415926, 4); -- 设置小数精度4位(四舍五入)
-- 随机数
select rand(); -- 完全随机
select rand(3); -- 设置随机数种子,设置种子后每次运行结果一致的
-- 绝对值
select abs(-3);
-- 求PI
select pi();-- 集合函数
-- 求元素个数
select size(work_locations) from test_array;
select size(members) from test_map;
-- 取出map的全部key
select map_keys(members) from test_map;
-- 取出map的全部value
select map_values(members) from test_map;
-- 查询array内是否包含指定元素,是就返回True
select * from test_array where ARRAY_CONTAINS(work_locations, 'tianjin');
-- 排序
select *, sort_array(work_locations) from test_array;-- 类型转换函数
-- 转二进制
select binary('hadoop');
-- 自由转换,类型转换失败报错或返回NULL
select cast('1' as bigint);-- 日期函数
-- 当前时间戳
select current_timestamp();
-- 当前日期
select current_date();
-- 时间戳转日期
select to_date(current_timestamp());
-- 年月日季度等
select year('2020-01-11');
select month('2020-01-11');
select day('2020-01-11');
select quarter('2020-05-11');
select dayofmonth('2020-05-11');
select hour('2020-05-11 10:36:59');
select minute('2020-05-11 10:36:59');
select second('2020-05-11 10:36:59');
select weekofyear('2020-05-11 10:36:59');
-- 日期之间的天数
select datediff('2022-12-31', '2019-12-31');
-- 日期相加、相减
select date_add('2022-12-31', 5);
select date_sub('2022-12-31', 5);
Hive内建了不少函数
- 使用
show functions
查看当下可用的所有函数; - 通过
describe function extended funcname
来查看函数的使用方式。
Mathematical Functions 数学函数 - 部分
----Mathematical Functions 数学函数-------------
--取整函数: round 返回double类型的整数值部分 (遵循四舍五入)
select round(3.1415926);
--指定精度取整函数: round(double a, int d) 返回指定精度d的double类型
select round(3.1415926,4);
--取随机数函数: rand 每次执行都不一样 返回一个0到1范围内的随机数
select rand();
--指定种子取随机数函数: rand(int seed) 得到一个稳定的随机数序列
select rand(3);
--求数字的绝对值
select abs(-3);
--得到pi值(小数点后15位精度)
select pi();
Collection Functions集合函数 - 全部
Type Conversion Functions类型转换函数 - 全部
Date Functions日期函数 - 部分
Conditional Functions条件函数 - 全部
String Functions字符串函数 - 部分
Data Masking Functions数据脱敏函数 - 部分
Misc. Functions其它函数 - 部分
十一 案例
11.1 需求分析
背景介绍
聊天平台每天都会有大量的用户在线,会出现大量的聊天数据,通过对聊天数据的统计分析,可以更好的对用户构建精准的用户画像,为用户提供更好的服务以及实现高ROI的平台运营推广,给公司的发展决策提供精确的数据支撑。
我们将基于一个社交平台App的用户数据,完成相关指标的统计分析并结合BI工具对指标进行可视化展现。
目标
基于Hadoop和Hive实现聊天数据统计分析,构建聊天数据分析报表
需求
- 统计今日总消息量
- 统计今日每小时消息量、发送和接收用户数
- 统计今日各地区发送消息数据量
- 统计今日发送消息和接收消息的用户数
- 统计今日发送消息最多的Top10用户
- 统计今日接收消息最多的Top10用户
- 统计发送人的手机型号分布情况
- 统计发送人的设备操作系统分布情况
数据内容
- 数据大小:30万条数据
- 列分隔符:Hive默认分隔符’\001’
- 数据字典及样例数据
建库建表
建库
--如果数据库已存在就删除
drop database if exists db_msg cascade ;
--创建数据库
create database db_msg ;
--切换数据库
use db_msg ;
--列举数据库
show databases ;
建表
--如果表已存在就删除
drop table if exists db_msg.tb_msg_source ;
--建表
create table db_msg.tb_msg_source(msg_time string comment "消息发送时间",sender_name string comment "发送人昵称",sender_account string comment "发送人账号",sender_sex string comment "发送人性别",sender_ip string comment "发送人ip地址",sender_os string comment "发送人操作系统",sender_phonetype string comment "发送人手机型号",sender_network string comment "发送人网络类型",sender_gps string comment "发送人的GPS定位",receiver_name string comment "接收人昵称",receiver_ip string comment "接收人IP",receiver_account string comment "接收人账号",receiver_os string comment "接收人操作系统",receiver_phonetype string comment "接收人手机型号",receiver_network string comment "接收人网络类型",receiver_gps string comment "接收人的GPS定位",receiver_sex string comment "接收人性别",msg_type string comment "消息类型",distance string comment "双方距离",message string comment "消息内容"
);
上传文件到Linux系统,load数据到表
load data local inpath '/home/hadoop/chat_data-30W.csv' overwrite into table tb_msg_source;
验证结果
select msg_time, sender_name, sender_ip, sender_phonetype, receiver_name, receiver_network from tb_msg_source limit 10;
11.2 ETL数据清洗
11.2.1 数据问题
- 问题1:当前数据中,有一些数据的字段为空,不是合法数据
select msg_time, sender_name, sender_gps from db_msg.tb_msg_source where length(sender_gps) = 0 limit 10;
- 问题2:需求中,需要统计每天、每个小时的消息量,但是数据中没有天和小时字段,只有整体时间字段,不好处理
select msg_time from db_msg.tb_msg_source limit 10;
- 问题3:需求中,需要对经度和维度构建地区的可视化地图,但是数据中GPS经纬度为一个字段,不好处理
select sender_gps from db_msg.tb_msg_source limit 10;
数据清洗的需求
- 需求1:对字段为空的不合法数据进行过滤 - where过滤
- 需求2:通过时间字段构建天和小时字段 - date hour函数
- 需求3:从GPS的经纬度中提取经度和维度 - split函数
- 需求4:将ETL以后的结果保存到一张新的Hive表中
create table db_msg.tb_msg_etl(msg_time string comment "消息发送时间",sender_name string comment "发送人昵称",sender_account string comment "发送人账号",sender_sex string comment "发送人性别",sender_ip string comment "发送人ip地址",sender_os string comment "发送人操作系统",sender_phonetype string comment "发送人手机型号",sender_network string comment "发送人网络类型",sender_gps string comment "发送人的GPS定位",receiver_name string comment "接收人昵称",receiver_ip string comment "接收人IP",receiver_account string comment "接收人账号",receiver_os string comment "接收人操作系统",receiver_phonetype string comment "接收人手机型号",receiver_network string comment "接收人网络类型",receiver_gps string comment "接收人的GPS定位",receiver_sex string comment "接收人性别",msg_type string comment "消息类型",distance string comment "双方距离",message string comment "消息内容",msg_day string comment "消息日",msg_hour string comment "消息小时",sender_lng double comment "经度",sender_lat double comment "纬度"
);
11.2.2 实现
INSERT OVERWRITE TABLE db_msg.tb_msg_etl
SELECT *, day(msg_time) as msg_day, HOUR(msg_time) as msg_hour, split(sender_gps, ',')[0] AS sender_lng,split(sender_gps, ',')[1] AS sender_lat
FROM tb_msg_source WHERE LENGTH(sender_gps) > 0;
查看结果
select msg_time, msy_day, msg_hour, sender_gps, sender_lng, sender_lat from db_msg.tb_msg_etl limit 10;
11.2.3 扩展概念:ETL
其实我们刚刚完成了从表tb_msg_source 查询数据进行数据过滤和转换,并将结果写入到:tb_msg_etl表中的操作,这种操作,本质上是一种简单的ETL行为。
ETL:
E,Extract,抽取
T,Transform,转换
L,Load,加载
从A抽取数据(E),进行数据转换过滤(T),将结果加载到B(L),就是ETL啦。ETL在大数据系统中是非常常见的,后续我们还会继续接触到它,目前简单了解一下即可。
11.3 指标计算
基于Hive完成需求的各个指标计算
需求
- 统计今日总消息量
- 统计今日每小时消息量、发送和接收用户数
- 统计今日各地区发送消息数据量
- 统计今日发送消息和接收消息的用户数
- 统计今日发送消息最多的Top10用户
- 统计今日接收消息最多的Top10用户
- 统计发送人的手机型号分布情况
- 统计发送人的设备操作系统分布情况
指标1:统计今日消息总量
--保存结果表
CREATE TABLE IF NOT EXISTS tb_rs_total_msg_cnt
COMMENT "每日消息总量" AS
SELECT msg_day, COUNT(*) AS total_msg_cnt
FROM db_msg.tb_msg_etl
GROUP BY msg_day;
指标2:统计每小时消息量、发送和接收用户数
--保存结果表
CREATE TABLE IF NOT EXISTS tb_rs_hour_msg_cnt
COMMENT "每小时消息量趋势" AS
SELECT msg_hour, COUNT(*) AS total_msg_cnt, COUNT(DISTINCT sender_account) AS sender_usr_cnt, COUNT(DISTINCT receiver_account) AS receiver_usr_cnt
FROM db_msg.tb_msg_etl GROUP BY msg_hour;
指标3:统计今日各地区发送消息总量
CREATE TABLE IF NOT EXISTS tb_rs_loc_cnt
COMMENT '今日各地区发送消息总量' AS
SELECT msg_day, sender_lng, sender_lat, COUNT(*) AS total_msg_cnt
FROM db_msg.tb_msg_etl
GROUP BY msg_day, sender_lng, sender_lat;
指标4:统计今日发送和接收用户人数
--保存结果表
CREATE TABLE IF NOT EXISTS tb_rs_usr_cnt
COMMENT "今日发送消息人数、接受消息人数" AS
SELECT
msg_day,
COUNT(DISTINCT sender_account) AS sender_usr_cnt,
COUNT(DISTINCT receiver_account) AS receiver_usr_cnt
FROM db_msg.tb_msg_etl
GROUP BY msg_day;
指标5:统计发送消息条数最多的Top10用户
--保存结果表
CREATE TABLE IF NOT EXISTS db_msg.tb_rs_s_user_top10
COMMENT "发送消息条数最多的Top10用户" AS
SELECT sender_name AS username, COUNT(*) AS sender_msg_cnt
FROM db_msg.tb_msg_etl
GROUP BY sender_name
ORDER BY sender_msg_cnt DESC
LIMIT 10;
指标6:统计接收消息条数最多的Top10用户
CREATE TABLE IF NOT EXISTS db_msg.tb_rs_r_user_top10
COMMENT "接收消息条数最多的Top10用户" AS
SELECT
receiver_name AS username,
COUNT(*) AS receiver_msg_cnt
FROM db_msg.tb_msg_etl
GROUP BY receiver_name
ORDER BY receiver_msg_cnt DESC
LIMIT 10;
指标7:统计发送人的手机型号分布情况
CREATE TABLE IF NOT EXISTS db_msg.tb_rs_sender_phone
COMMENT "发送人的手机型号分布" AS
SELECT sender_phonetype, COUNT(sender_account) AS cnt
FROM db_msg.tb_msg_etl
GROUP BY sender_phonetype;
指标8:统计发送人的手机操作系统分布
--保存结果表
CREATE TABLE IF NOT EXISTS db_msg.tb_rs_sender_os
COMMENT "发送人的OS分布" AS
SELECTsender_os, COUNT(sender_account) AS cnt
FROM db_msg.tb_msg_etl
GROUP BY sender_os
十二 可视化展现
12.1 BI工具
BI:Business Intelligence,商业智能。
指用现代数据仓库技术、线上分析处理技术、数据挖掘和数据展现技术进行数据分析以实现商业价值。
简单来说,就是借助BI工具,可以完成复杂的数据分析、数据统计等需求,为公司决策带来巨大的价值。
所以,一般提到BI,我们指代的就是工具软件。常见的BI软件很多,比如:
- FineBI
- SuperSet
- PowerBI
- TableAu
- 等
12.2 FineBI的介绍及安装
FineBI的介绍:https://www.finebi.com/
FineBI 是帆软软件有限公司推出的一款商业智能(Business Intelligence)产品。FineBI 是定位于自助大数据分析的 BI 工具,能够帮助企业的业务人员和数据分析师,开展以问题导向的探索式分析。
FineBI的特点
- 通过多人协作来实现最终的可视化构建
- 不需要通过复杂代码来实现开发,通过可视化操作实现开发
- 适合于各种数据可视化的应用场景
- 支持各种常见的分析图表和各种数据源
- 支持处理大数据
启动登陆
目录、首页大屏及帮助文档
仪表盘:用于构建所有可视化报表
数据准备:用于配置各种报表的数据来源
管理系统:用于管理整个FineBI的使用:用户管理、数据源管理、插件管理、权限管理等
FineBI与Hive集成的官方文档:https://help.fanruan.com/finebi/doc-view-301.html
驱动配置
- 问题:如果使用FineBI连接Hive,读取Hive的数据表,需要在FineBI中添加Hive的驱动jar包
- 解决:将Hive的驱动jar包放入FineBI的lib目录下(webapps\webroot\WEB-INF\lib)
插件安装
- 问题:我们自己放的Hive驱动包会与FineBI自带的驱动包产生冲突,导致FineBI无法识别我们自己的驱动包
- 解决:安装FineBI官方提供的驱动包隔离插件:隔离插件下载
安装插件
构建连接
保存连接
数据准备
新建分组
添加业务包
添加表
更新业务包
12.3可视化展现
基于FineBI完成指标的可视化展现
创建报表
选择仪表板样式
添加标题
编辑标题文本框(注意字体大小、居中、文本框位置可调整)
添加文本内容(1/10)
添加文本内容(2/10)
添加文本内容(3/10)
添加文本内容(4/10)
添加文本内容(5/10)
添加文本内容(6/10)
添加文本内容(7/10)
添加文本内容(8/10)
添加文本内容(9/10)
添加文本内容(10/10),同理添加总发送消息人数和总接收消息人数
添加地图(1/9)
添加地图(2/9)
添加地图(3/9)
添加地图(4/9)
添加地图(5/9)
添加地图(6/9)
添加地图(7/9)
添加地图(8/9)
添加地图(9/9)
添加雷达图(1/5)
添加雷达图(2/5)
添加雷达图(3/5)
添加雷达图(4/5)
添加雷达图(5/5)
添加柱状图(1/5)
添加柱状图(2/5)
添加柱状图(3/5)
添加柱状图(4/5)
添加柱状图(5/5)
添加环饼状图(1/6)
添加环饼状图(2/6)
添加环饼状图(3/6)
添加环饼状图(4/6)
添加环饼状图(5/6)
添加环饼状图(6/6)
添加词汇云图(1/5)
添加词汇云图(2/5)
添加词汇云图(3/5)
添加词汇云图(4/5)
添加词汇云图(5/5)
添加趋势曲线图(1/5)
添加趋势曲线图(2/5)
添加趋势曲线图(3/5)
添加趋势曲线图(4/5)
添加趋势曲线图(5/5)
报表预览