注:在对hive的概念、优缺点、安装部署和参数配置在之后再进行总结,本小节主要对hive中的分组和分区进行总结。
一、分组
1、group by语句
group by通常和聚合函数一起使用,按照一个或者多个列进行分组,然后对每个组进行聚合操作。
例如:计算员工表中每个部门中每个岗位的最高工资
注:在查询过程中只能查询group by后的字段或者函数中的字段。
select t.deptno, t.job, max(t.sal) max_sal from emp t group by t.deptno, t.job;
2、having语句
在一般的分组函数后不能使用查询where语句,而having后面可以使用分组函数,having只用于group by分组统计语句。
select 字段 from 表名 where 条件 group by 字段
或者
select 字段 from 表名 group by 字段 having 过滤条件
注意:对于过滤条件,可以先用where,再用group by或者是先用group by,再用having
例如:求每个部门的平均薪水大于 2000 的部门
select deptno, avg(sal) avg_sal from emp group by deptno having avg_sal > 2000;
而不使用having语句的语句为:
select deptno, avg_sal from (select deptno,avg(sal) avg_sal from emp group by deptno)t1 where avg_sal > 2000;
二、两表连接(join)
join主要对两个表通过两个相同的字段进行连接,并查询相关的结果
1、等值join
例如:根据员工表和部门表中的部门编号相等,查询员工编号、员工名称和部门名称;
select e.empno, e.ename, d.deptno, d.dname from emp e join dept d on e.deptno=d.deptno;
2、内连接
内连接:只有进行连接的两个表中都存在与连接条件相匹配的数据才会被保留下来。
例如:查询两个表相同字段匹配的数据
select e.empno, e.ename, d.deptno from emp e join dept d on e.deptno = d.deptno;
3、左外连接
左外连接:JOIN 操作符左边表中符合 WHERE 子句的所有记录将会被返回。
select e.empno, e.ename, d.deptno from emp e left join dept d on e.deptno = d.deptno;
4、右外连接
右外连接:JOIN 操作符右边表中符合 WHERE 子句的所有记录将会被返回。
select e.empno, e.ename, d.deptno from emp e right join dept d on e.deptno = d.deptno;
5、满外连接
满外连接:将会返回所有表中符合 WHERE 语句条件的所有记录。如果任一表的指定字 段没有符合条件的值的话,那么就使用 NULL 值替代。
select e.empno, e.ename, d.deptno from emp e full join dept d on e.deptno = d.deptno;
6、多表连接
注意:连接 n 个表,至少需要 n-1 个连接条件。例如:连接三个表,至少需要两个连接 条件。
SELECT e.ename, d.dname, l.loc_name FROM emp e JOIN dept d ON d.deptno = e.deptno
JOIN location l ON d.loc = l.loc;
7、笛卡尔积
在具体的项目中应该尽量避免这种操作,防止运行内存卡顿。
select empno, dname from emp, dept;
三、排序
排序:对表中的数据进行对某个字段进行排序
1、全局排序(order by)
order by:全局排序,只有一个reduce,因此在表中数据非常大的情况下,运行时间会很长,再实际操作中使用很少。
ASC(ascend): 升序(默认) DESC(descend): 降序
例如:查询员工信息按工资升序(降序)排列
select * from emp order by sal(desc);
多个列进行排序
select ename, deptno, sal from emp order by deptno, sal;
2、每个reduce内部排序(sort by)
Sort By:对于大规模的数据集 order by 的效率非常低。在很多情况下,并不需要全局排 序,此时可以使用 sort by。
Sort by 为每个 reducer 产生一个排序文件。每个 Reducer 内部进行排序,对全局结果集 来说不是排序。
1)设置reduce个数
set mapreduce.job.reduces=3;
2)查看设置reduce个数
set mapreduce.job.reduces;
3)根据部门编号降序查看员工信息
select * from emp sort by deptno desc;
4)将查询结果导入到文件中
insert overwrite local directory '/opt/module/data/sortby-result' select * from emp sort by deptno desc;
3、分区排序(distribute by)
Distribute By: 在有些情况下,我们需要控制某个特定行应该到哪个 reducer,通常是为 了进行后续的聚集操作。distribute by 子句可以做这件事。distribute by 类似 MR 中 partition (自定义分区),进行分区,结合 sort by 使用。
注:对于 distribute by 进行测试,一定要分配多 reduce 进行处理,否则无法看到 distribute by 的效果。
insert overwrite local directory
'/opt/module/data/distribute-result' select * from emp distribute by
deptno sort by empno desc;
注意: ➢ distribute by 的分区规则是根据分区字段的 hash 码与 reduce 的个数进行模除后, 余数相同的分到一个区。
➢ Hive 要求 DISTRIBUTE BY 语句要写在 SORT BY 语句之前。
4、Cluster by
当 distribute by 和 sorts by 字段相同时,可以使用 cluster by 方式。
cluster by 除了具有 distribute by 的功能外还兼具 sort by 的功能。但是排序只能是升序 排序,不能指定排序规则为 ASC 或者 DESC。
select * from emp cluster by deptno;
等价
select * from emp distribute by deptno sort by deptno;
四、分区表
分区表实际上就是对应一个 HDFS 文件系统上的独立的文件夹,该文件夹下是该分区所 有的数据文件。Hive 中的分区就是分目录,把一个大的数据集根据业务需要分割成小的数据 集。在查询时通过 WHERE 子句中的表达式选择查询所需要的指定的分区,这样的查询效率 会提高很多。
1、分区表基本操作
1)创建分区表
create table dept_par(deptno int,dname string,loc string) partitioned by(day string)
row format delimited fields terminated by '\t';
注:分区表设置的分区字段要区别于表中字段
2)加载数据到分区表(数据准备,在本地创建文件数据)
dept_20220225.txt
10 ACCOUNTING 1700
20 RESEARCH 1800
dept_20220226.txt
30 SALES 1900
40 OPERATIONS 1700
dept_20220227.txt
50 TEST 2000
60 DEV 1900
加载数据到具体的分区
load data inpath '/opt/module/hive/data/dept_20220225.txt' into table dept_par partition(day='20220225');
load data inpath '/opt/module/hive/data/dept_20220226.txt' into table dept_par partition(day='20220226');
load data inpath '/opt/module/hive/data/dept_20220227.txt' into table dept_par partition(day='20220227');
即可在hdfs集群上查看加载的分区数据
3)分区表数据查询
单分区数据查询
select * from dept_partition where day='20220225';
多分区数据查询
select * from dept_partition where day='20220225'
union
select * from dept_partition where day='20220226'
union
select * from dept_partition where day='20220227';
4) 增加分区
alter table dept_par add partition(day='20220228');
创建多个分区
alter table dept_partition add partition(day='20220224')
partition(day='20220223');
5)删除分区
alter table dept_partition drop partition (day='20220224');
同时删除多个分区
alter table dept_partition drop partition (day='20220223'), partition(day='20220228');
注:删除和增加分区以逗号区分
6)查看分区表信息
分区表结构
desc formatted dept_partition;
查看多少个分区
show partitions dept_partition;
2、二级分区
如果一天的数据量也很大的情况下,就要在每天下面在对每个小时的数据进行分区
1)创建二级分区
create table dept_partition2(
deptno int, dname string, loc string
)
partitioned by (day string, hour string)
row format delimited fields terminated by '\t';
2)加载数据
load data local inpath
'/opt/module/hive/datas/dept_20220225.txt' into table
dept_partition2 partition(day='20220225', hour='12');
3)查看分区数据
select * from dept_partition2 where day='20220225' and hour='12';
3、动态分区调整(即分区的数据按照表字段来分区)
关系型数据库中,对分区表 Insert 数据时候,数据库自动会根据分区字段的值,将数据 插入到相应的分区中,Hive 中也提供了类似的机制,即动态分区(Dynamic Partition),只不过, 使用 Hive 的动态分区,需要进行相应的配置。
1)设置为非严格模式(动态分区的模式,默认 strict,表示必须指定至少一个分区为 静态分区,nonstrict 模式表示允许所有的分区字段都可以使用动态分区。)
set hive.exec.dynamic.partition.mode=nonstrict
例如::将 dept 表中的数据按照地区(loc 字段),插入到目标表 dept_partition 的相应 分区中。
创建目标分区表
create table dept_par4(id int, name string)
partitioned by (loc int) row format delimited fields terminated by '\t';
设置动态分区
insert into table dept_par4 partition(loc) select deptno, dname, loc from dept;
注:查询语句的最后字段默认为分区字段。
五、分桶表
分区提供一个隔离数据和优化查询的便利方式。不过,并非所有的数据集都可形成合理 的分区。对于一张表或者分区,Hive 可以进一步组织成桶,也就是更为细粒度的数据范围 划分。
分桶是将数据集分解成更容易管理的若干部分的另一个技术。
分区针对的是数据的存储路径;分桶针对的是数据文件。
1)创建分通表
create table stu_buck(id int, name string)
clustered by(id)
into 4 buckets
row format delimited fields terminated by '\t';
2)导入数据(hdfs上的数据)
load data inpath '/student.txt' into table stu_buck;
注:Hive 的分桶采用对分桶字段的值进行哈希,然后除以桶的个数求余的方 式决定该条记录存放在哪个桶当中。
2、抽样查询
对于非常大的数据集,有时用户需要使用的是一个具有代表性的查询结果而不是全部结 果。Hive 可以通过对表进行抽样来满足这个需求。
语法: TABLESAMPLE(BUCKET x OUT OF y)(注意:x 的值必须小于等于 y 的值)
查询表 stu_buck 中的数据(从第一个桶开始到第四个桶(4表示将全部数据随机分为四份),随机抽取一个桶的数据)。
select * from stu_buck tablesample(bucket 1 out of 4 on id);