打怪升级之小白的大数据之旅(六十五)

Hive旅程第六站:Hive的查询

上次回顾

经过前面的学习,我们已经可以初步使用Hive对数据的一些简单操作了,本章节是Hive的一个重点内容–查询

查询

HQL和我们前面学习的Mysql语法是一样的,只是在一些特定的查询会有所区别,比如分桶表、分区表、创建表这些

查询相关的API来源自官网:https://cwiki.apache.org/confluence/display/Hive/LanguageManual+Select

查询的整个学习都围绕下面的语法格式:

SELECT [ALL | DISTINCT] select_expr, select_expr, ...
  FROM table_reference
  [WHERE where_condition]
  [GROUP BY col_list]
  [ORDER BY col_list]
  [CLUSTER BY col_list
    | [DISTRIBUTE BY col_list] [SORT BY col_list]
  ]
 [LIMIT number]

查询可以分为四个部分,分别是基本查询、分组、Join语句和排序,下面开始正式进入Hive的查询旅程

基本查询

语法格式:

select filed1,filed2 from  tableName

测试数据(创建两张表,员工表emp和部门表dept):

-- 部门表
vim $HIVE_HOME/dbdata/company/dept.txt
10	ACCOUNTING	1700
20	RESEARCH	1800
30	SALES	1900
40	OPERATIONS	1700
-- 员工表
vim $HIVE_HOME/dbdata/company/emp.txt
7369	SMITH	CLERK	7902	1980-12-17	800.00		20
7499	ALLEN	SALESMAN	7698	1981-2-20	1600.00	300.00	30
7521	WARD	SALESMAN	7698	1981-2-22	1250.00	500.00	30
7566	JONES	MANAGER	7839	1981-4-2	2975.00		20
7654	MARTIN	SALESMAN	7698	1981-9-28	1250.00	1400.00	30
7698	BLAKE	MANAGER	7839	1981-5-1	2850.00		30
7782	CLARK	MANAGER	7839	1981-6-9	2450.00		10
7788	SCOTT	ANALYST	7566	1987-4-19	3000.00		20
7839	KING	PRESIDENT		1981-11-17	5000.00		10
7844	TURNER	SALESMAN	7698	1981-9-8	1500.00	0.00	30
7876	ADAMS	CLERK	7788	1987-5-23	1100.00		20
7900	JAMES	CLERK	7698	1981-12-3	950.00		30
7902	FORD	ANALYST	7566	1981-12-3	3000.00		20
7934	MILLER	CLERK	7782	1982-1-23	1300.00		10

创建表

-- 创建部门表
create table if not exists dept(
deptno int,
dname string,
loc int
)
row format delimited fields terminated by '\t';

-- 创建员工表
create table if not exists emp(
empno int,
ename string,
job string,
mgr int,
hiredate string, 
sal double, 
comm double,
deptno int)
row format delimited fields terminated by '\t';

加载/导入数据

load data local inpath '/opt/module/hive/dbdata/company/dept.txt' into table
dept;
load data local inpath '/opt/module/hive/dbdata/company/emp.txt' into table emp;

测试数据和表准备完成,我们开始进行实际查询操作

全表查询

查询所有员工信息

select * from emp;

选择特定列查询

查询员工的名字和编号信息

select empno, ename from emp;

列别名

列别名同Mysql中学习的一样:
1)重命名一个列
2)便于计算
3)两种方式: 紧跟列名(需要空格隔开),也可以在列名和别名之间加入关键字‘AS’

算术运算符

运算符

描述

A + B

A和B 相加

A - B

A减去B

A * B

A和B 相乘

A / B

A除以B

A % B

A对B取余

A & B

A和B按位取与

A | B

A和B按位取或

A ^ B

A和B按位取异或

~A

A按位取反

示例:查询出所有员工的薪水后加1000并显示

select sal + 1000 from emp;

常用函数

count()求所有员工数量

select count(*) emp_countfrom emp;

max()求工资的最大值

select max(*) max_sal from emp;

min()求工资的最小值

select min(*) min_sal from emp;

sum()求工资的总和

select sum(*) sum_sal from emp;

avg()求工资的平均值

select avg(*) avg_sal from emp;

注意: 基本查询中的常用函数也称之为聚合函数,如果我们在下面使用Group by 进行分组时,需要将该聚合函数之前的字段全部放到Group中,否则会报错

Limit语句

Limit常用于数据量大的时候进行分页查询,分页公式Mysql中有写,另外limit默认是从第一条数据开始读取

查询员工表的第1条到第5条数据

select * from emp limit 5;

查询员工表的第2条数据之后的3条数据

select * from emp limit 2,3;

Where语句

使用WHERE子句,将不满足条件的行过滤掉并且WHERE子句紧随FROM子句

查询出薪水大于1000的所有员工

select * from emp where sal >1000;

注意:where子句中不能使用字段别名

比较运算符

比较运算符可以使用在Join…on和Having语句中

操作符

支持的数据类型

描述

A=B

基本数据类型

如果A等于B则返回TRUE,反之返回FALSE

A<=>B

基本数据类型

如果A和B都为NULL,则返回TRUE,如果一边为NULL,返回False

A<>B, A!=B

基本数据类型

A或者B为NULL则返回NULL;如果A不等于B,则返回TRUE,反之返回FALSE

A<B

基本数据类型

A或者B为NULL,则返回NULL;如果A小于B,则返回TRUE,反之返回FALSE

A<=B

基本数据类型

A或者B为NULL,则返回NULL;如果A小于等于B,则返回TRUE,反之返回FALSE

A>B

基本数据类型

A或者B为NULL,则返回NULL;如果A大于B,则返回TRUE,反之返回FALSE

A>=B

基本数据类型

A或者B为NULL,则返回NULL;如果A大于等于B,则返回TRUE,反之返回FALSE

A [NOT] BETWEEN B AND C

基本数据类型

如果A,B或者C任一为NULL,则结果为NULL。如果A的值大于等于B而且小于或等于C,则结果为TRUE,反之为FALSE。如果使用NOT关键字则可达到相反的效果。

A IS NULL

所有数据类型

如果A等于NULL,则返回TRUE,反之返回FALSE

A IS NOT NULL

所有数据类型

如果A不等于NULL,则返回TRUE,反之返回FALSE

IN(数值1, 数值2)

所有数据类型

使用 IN运算显示列表中的值

A [NOT] LIKE B

STRING 类型

B是一个SQL下的简单正则表达式,也叫通配符模式,如果A与其匹配的话,则返回TRUE;反之返回FALSE。B的表达式说明如下:‘x%’表示A必须以字母‘x’开头,‘%x’表示A必须以字母’x’结尾,而‘%x%’表示A包含有字母’x’,可以位于开头,结尾或者字符串中间。如果使用NOT关键字则可达到相反的效果。

A RLIKE B, A REGEXP B

STRING 类型

B是基于java的正则表达式,如果A与其匹配,则返回TRUE;反之返回FALSE。匹配使用的是JDK中的正则表达式接口实现的,因为正则也依据其中的规则。例如,正则表达式必须和整个字符串A相匹配,而不是只需与其字符串匹配

查询出薪水等于5000的所有员工

select * from emp where sal =5000;

查询工资在500到1000的员工信息

select * from emp where sal between 500 and 1000;

查询comm为空的所有员工信息

select * from emp where comm is null;

查询工资是1500或5000的员工信息

select * from emp where sal IN (1500, 5000);

逻辑运算符

逻辑运算符也和Myql中学习的一样,直接根据需求使用即可

操作符

含义

AND

逻辑并

OR

逻辑或

NOT

逻辑否

查询薪水大于1000,部门是30

select * from emp where sal>1000 and deptno=30;

查询薪水大于1000,或者部门是30

select * from emp where sal>1000 or deptno=30;

查询除了20部门和30部门以外的员工信息

select * from emp where deptno not IN(30, 20);

Like和RLike

我们再次回顾一下Mysql中学习的Like

  • 使用LIKE运算选择类似的值
  • 选择条件可以包含字符或数字:
  • % 代表零个或多个字符(任意个字符)
  • _ 代表一个字符

RLike是Hive中独有的一个语句,它可以通过Java的正则表达式来进行指定的匹配

查找名字以A开头的员工信息

select * from emp where ename LIKE 'A%';
select * from emp where ename RLIKE '^A';

查找名字中第二个字母为A的员工信息

select * from emp where ename LIKE '_A%';
select * from emp where ename RLIKE '^.A';

查找名字中带有A的员工信息

select * from emp where ename  LIKE '%A%';
select * from emp where ename  RLIKE '[A]';

分组

Group By

GROUP BY语句通常会和聚合函数一起使用,按照一个或者多个列队结果进行分组,然后对每个组执行聚合操作

计算每个部门的平均薪水

select 
	t.deptno,
	avg(t.sal) avg_sal
from emp t
group by t.deptno;

计算emp每个部门中每个岗位的最高薪水

select 
	t.deptno,
	t.job,
	max(t.sal) max_sal
from emp t
group by t.deptno, t.job;

Having

having 和 where 用法相同,都是对数据进行过滤,但是它们在HQL语法中很严格

  • where后面不能写分组聚合函数,而having后面可以使用分组聚合函数
  • having只用于group by分组统计语句

求部门平均薪水大于2000的部门

select 
	deptno,
	avg(sal) avg_sal
from emp
group by deptno
having avg_sal > 2000;

Join语句

学习Join语句前,我们再次思考回顾一下学习Mysql时的那几个连接,内连接、左外连接、右外连接等。。忘了连接原理的小伙伴可以回顾看一下那一篇的博客:

连接总共有七种,我们常用的有四种

hive的递归查WITH RECURSIVE hive 迭代查询_hadoop

等值Join

  • Hive支持通常的SQL JOIN语句,但是只支持等值连接,不支持非等值连接

根据员工表和部门表中的部门编号相等,查询员工编号、员工名称和部门名称

select 
	e.empno,
	e.ename,
	d.deptno,
	d.dname
from emp
join dept
on emp.deptno = dept.deptno;

表的别名

因为我们工作中HQL语句可能会很长,所以为了简化查询,理清我们HQL语句的逻辑,我们通常情况下会采用别名,并且使用表名前缀可以提高执行效率

合并员工表和部门表

select 
	e.empno,
	e.ename,
	d.deptno
from emp e
join dept d
on e.deptno = d.deptno;

内连接

  • 内连接:只有进行连接的两个表中都存在与连接条件相匹配的数据才会被保留下来
  • hive中连接方式同样默认是内连接

    使用内连接连接员工表和部门表
select 
	e.empno,
	e.ename,
	d.deptno
from emp e
join dept d
on e.deptno = d.deptno;

左外连接

  • 左外连接:JOIN操作符左边表中符合WHERE子句的所有记录将会被返回

使用左外连接查询有部门的员工信息(例如:老板没有部门)

select
	e.empno, 
	e.ename, 
	d.deptno 
from emp e 
left join dept d 
on e.deptno = d.deptno;

右外连接

  • 右外连接:JOIN操作符右边表中符合WHERE子句的所有记录将会被返回

    使用右外连接查询有部门的员工信息(例如:老板没有部门)
select 
	e.empno, 
	e.ename, 
	d.deptno 
from emp e 
right join dept d 
on e.deptno = d.deptno;

满外连接

  • 满外连接:将会返回所有表中符合WHERE语句条件的所有记录。如果任一表的指定字段没有符合条件的值的话,那么就使用NULL值替代

查询所有员工数据,包括没有分配部门的员工

select 
	e.empno, 
	e.ename, 
	d.deptno 
from emp e 
full join dept d 
on e.deptno = d.deptno

多表连接

  • 大多数情况下,Hive会对每对JOIN连接对象启动一个MapReduce任务
  • 下面栗子中会首先启动一个MapReduce job 对表 e 和表 d 进行连接操作
  • 然后会再启动一个MapReduce job将第一个MapReduce job的输出和表 l ;进行连接操作。
  • 为什么不是表d和表 l 先进行连接操作呢?这是因为Hive总是按照从左到右的顺序执行的
  • 当对3个或者更多表进行join连接时,如果每个on子句都使用相同的连接键的话,那么只会产生一个MapReduce job

多表连接,我们就需要再录入一个地址location的数据

测试数据:

vim $HIVE_HOME/dbdata/company/location.txt
1700	Beijing
1800	London
1900	Tokyo

创建表

create table if not exists location(
loc int,
loc_name string
)
row format delimited fields terminated by '\t';

加载/导入数据

load data local inpath '/opt/module/hive/dbdata/company/location.txt' into table location;

查询出员工的信息、部门信息、和地址信息

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;

笛卡尔积

笛卡尔积在mysql中学习过,我们再来回顾一下:

  • 笛卡尔集会在下面条件下产生
  • 省略连接条件
  • 连接条件无效
  • 所有表中的所有行互相连接

笛卡尔积的示例

select empno, dname from emp, dept;

排序

全局排序

  • Order By:全局排序,在MR底层,只有一个Reducer
  • 排序规则和Mysql一样,升序(默认)和倒序 asc/desc
  • ORDER BY 子句在SELECT语句的结尾使用

查询员工姓名、工资并按工资升序排列

select 
	dname, 
	sal 
from emp 
order by sal;

查询员工姓名、工资并按工资降序排列

select 
	dname, 
	sal 
from emp 
order by sal desc;

按照别名排序

按照员工薪水的2倍排序

select 
	ename, 
	deptno, 
	sal 
from emp 
order by deptno, sal ;

多个列排序

  • 多个列排序的顺序按照我们书写的顺序进行排序

按照部门和工资升序排序

select 
	ename, 
	deptno, 
	sal 
from emp 
order by deptno, sal ;

内部排序

  • 内部排序使用的语法是 sort by
  • 内部排序指的是每个Reducer的内部进行排序,
  • Sort By:对于大规模的数据集order by的效率非常低。在很多情况下,并不需要全局排序,此时可以使用sort by
  • Sort by为每个Reducer产生一个排序文件。每个Reducer内部进行排序,对全局结果集来说不是排序
  • 通常情况下,我们会配合分区进行排序,可以提高我们的运行效率

示例:
1.设置Reducer个数(为了便于查看,所以分文件存储)

set mapreduce.job.reduces=3;

2.根据部门编号降序查看员工姓名、部门、以及薪资信息

select * from emp sort by deptno desc;

3.将查询结果导入到文件中(因为我们看着不明显)

insert overwrite local directory '/opt/module/hive/dbdata/sortby-result'
row format delimited fields terminated by "\t"
select ename, sal, deptno  from emp sort by deptno desc;

运行后,会发现产生的三个文件,每个文件都是按照部门降序存储的

hive的递归查WITH RECURSIVE hive 迭代查询_java_02


查看一下其中一个文件

hive的递归查WITH RECURSIVE hive 迭代查询_java_03

分区

  • 上面的自定义Reduce并分别输出到三个文件中算是一个伪分区,我们想要根据指定的规则进行分区就需要了解如何使用 Distribute By
  • 在有些情况下,我们需要控制某个特定行应该到哪个reducer,通常是为了进行后续的聚集操作
  • distribute by 子句可以做这件事\
  • distribute by类似MR中partition(自定义分区),进行分区,结合sort by使用
  • 对于distribute by进行测试,一定要分配多reduce进行处理,否则无法看到distribute by的效果

先按照部门编号分区,再按照员工编号降序排序,同样还是使用指定Reduce写入三个文件然后进行查看分区后的结果

insert overwrite local directory '/opt/module/hive/dbdata/distribute-result' row format delimited fields terminated by "\t" select ename,empno, sal, deptno from emp distribute by deptno sort by empno desc;

查看结果可以发现,我们的文件是按照部门编号分别进行存储的(仅展示其中一个)

hive的递归查WITH RECURSIVE hive 迭代查询_数据库_04

注意:

  • distribute by的分区规则是根据分区字段的hash码与reduce的个数进行模除后,余数相同的分到一个区。
  • Hive要求DISTRIBUTE BY语句要写在SORT BY语句之前。
  • 演示完以后mapreduce.job.reduces的值要设置回-1,否则下面分区or分桶表load跑mr的时候有可能会报错

分区排序

  • 当distribute by和sort by字段相同时,可以使用cluster by方式
  • cluster by除了具有distribute by的功能外还兼具sort by的功能。但是排序只能是升序排序,不能指定排序规则为ASC或者DESC

原来的分区+sort排序

select * from emp distribute by deptno sort by deptno;

使用cluster by进行排序

select * from emp cluster by deptno;

总结

呼,查询的相关HQL语句基本上写完了,内容很多,大家记不住没关系,有个印象,当需要的时候在这篇博客中搜索查找即可,下一章我为大家带来分区表和分桶表的知识点