BigData-Notes/notes/Hive数据查询详解.md
2019-04-29 17:43:05 +08:00

408 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Hive数据查询详解
<nav>
<a href="#一数据准备">一、数据准备</a><br/>
<a href="#二单表查询">二、单表查询</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#21-SELECT">2.1 SELECT</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#22-WHERE">2.2 WHERE</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#23--DISTINCT">2.3 DISTINCT</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#24-分区查询">2.4 分区查询</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#25-LIMIT">2.5 LIMIT</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#26-GROUP-BY">2.6 GROUP BY</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#27-ORDER-AND-SORT">2.7 ORDER AND SORT</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#28-HAVING">2.8 HAVING</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#29-DISTRIBUTE-BY">2.9 DISTRIBUTE BY</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#210-CLUSTER-BY">2.10 CLUSTER BY</a><br/>
<a href="#三多表联结查询">三、多表联结查询</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#31-INNER-JOIN">3.1 INNER JOIN</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#32-LEFT-OUTER--JOIN">3.2 LEFT OUTER JOIN </a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#33-RIGHT-OUTER--JOIN">3.3 RIGHT OUTER JOIN</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#34-FULL-OUTER--JOIN">3.4 FULL OUTER JOIN </a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#35-LEFT-SEMI-JOIN">3.5 LEFT SEMI JOIN</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#36-JOIN">3.6 JOIN</a><br/>
<a href="#四JOIN优化">四、JOIN优化</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#41-STREAMTABLE">4.1 STREAMTABLE</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#42-MAPJOIN">4.2 MAPJOIN</a><br/>
<a href="#五SELECT的其他用途">五、SELECT的其他用途</a><br/>
<a href="#六本地模式">六、本地模式</a><br/>
</nav>
## 一、数据准备
为了演示查询操作,这里需要预先创建两张表,并加载数据。
> 这里的表结构主要参考自Oracle内置的练习表——emp表和dept表涉及到数据文件emp.txt和dept.txt可以在本仓库的sources目录下下载。
### 1.1 员工表
```sql
-- 建表语句
CREATE TABLE emp(
empno INT, -- 员工表编号
ename STRING, -- 员工姓名
job STRING, -- 职位类型
mgr INT,
hiredate TIMESTAMP, --雇佣日期
sal DECIMAL(7,2), --工资
comm DECIMAL(7,2),
deptno INT) --部门编号
ROW FORMAT DELIMITED FIELDS TERMINATED BY "\t";
--加载数据
LOAD DATA LOCAL INPATH "/usr/file/emp.txt" OVERWRITE INTO TABLE emp;
```
### 1.2 部门表
```sql
-- 建表语句
CREATE TABLE dept(
deptno INT, --部门编号
dname STRING, --部门名称
loc STRING --部门所在的城市
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY "\t";
--加载数据
LOAD DATA LOCAL INPATH "/usr/file/dept.txt" OVERWRITE INTO TABLE dept;
```
### 1.3 分区表
这里需要额外创建一张分区表,主要是为了演示分区查询:
```sql
CREATE EXTERNAL TABLE emp_ptn(
empno INT,
ename STRING,
job STRING,
mgr INT,
hiredate TIMESTAMP,
sal DECIMAL(7,2),
comm DECIMAL(7,2)
)
PARTITIONED BY (deptno INT) -- 按照部门编号进行分区
ROW FORMAT DELIMITED FIELDS TERMINATED BY "\t";
--加载数据
LOAD DATA LOCAL INPATH "/usr/file/emp.txt" OVERWRITE INTO TABLE emp_ptn PARTITION (deptno=20)
LOAD DATA LOCAL INPATH "/usr/file/emp.txt" OVERWRITE INTO TABLE emp_ptn PARTITION (deptno=30)
LOAD DATA LOCAL INPATH "/usr/file/emp.txt" OVERWRITE INTO TABLE emp_ptn PARTITION (deptno=40)
LOAD DATA LOCAL INPATH "/usr/file/emp.txt" OVERWRITE INTO TABLE emp_ptn PARTITION (deptno=50)
```
## 二、单表查询
### 2.1 SELECT
```sql
-- 查询表中全部数据
SELECT * FROM emp;
```
### 2.2 WHERE
```sql
-- 查询10号部门中员工编号大于 7782 的员工信息
SELECT * FROM emp WHERE empno > 7782 AND deptno = 10;
```
### 2.3 DISTINCT
Hive支持使用DISTINCT关键字去重。
```sql
-- 查询所有工作类型
SELECT DISTINCT job FROM emp;
```
### 2.4 分区查询
分区查询(Partition Based Queries),可以指定某个分区或者分区范围。
```sql
-- 查询分区表中部门编号在[20,40]之间的员工
SELECT emp_ptn.* FROM emp_ptn
WHERE emp_ptn.deptno >= 20 AND emp_ptn.deptno <= 40;
```
### 2.5 LIMIT
```sql
-- 查询薪资最高的5名员工
SELECT * FROM emp ORDER BY sal DESC LIMIT 5;
```
### 2.6 GROUP BY
Hive支持使用GROUP BY进行分组聚合操作。
```sql
set hive.map.aggr=true;
-- 查询各个部门薪酬综合
SELECT deptno,SUM(sal) FROM emp GROUP BY deptno;
```
`hive.map.aggr`控制程序如何进行聚合。默认值为false。如果设置为trueHive会在map任务中就执行一次聚合。这可以提高聚合效率但需要消耗更多内存。
### 2.7 ORDER AND SORT
可以使用ORDER BY或者Sort BY对查询结果进行排序排序字段可以是整型也可以是字符串如果是整型则按照大小排序如果是字符串则按照字典序排序。ORDER BY 和 Sort BY 的区别如下:
+ 使用ORDER BY时会有一个Reducer对全部查询结果进行排序能保证数据的全局有序性
+ 使用Sort BY时只会在每个Reducer中进行排序这可以保证每个Reducer的输出数据时有序的但是并不能保证全局有序。
由于ORDER BY的操作时间可能过长如果你设置了严格模式(hive.mapred.mode = strict),则其后面必须再跟一个`limit`子句。
> hive.mapred.mode默认值是nonstrict ,也就是非严格模式。
```sql
-- 查询员工工资,结果按照部门升序,按照工资降序排列
SELECT empno, deptno, sal FROM emp ORDER BY deptno ASC, sal DESC;
```
### 2.8 HAVING
可以使用HAVING对分组数据进行过滤。
```sql
-- 查询工资总和大于9000的所有部门
SELECT deptno,SUM(sal) FROM emp GROUP BY deptno HAVING SUM(sal)>9000;
```
### 2.9 DISTRIBUTE BY
默认情况下MapReduce程序会对Map输出结果的Key值进行散列并均匀分发到所有Reducer上。如果想要把具有相同Key值的数据分发到同一个Reducer进行处理这就需要使用DISTRIBUTE BY字句。
需要注意的是DISTRIBUTE BY虽然能保证具有相同Key值的数据分发到同一个Reducer但是不能保证数据在Reducer上是有序的。情况如下
把以下5个数据发送到两个Reducer上进行处理
```properties
k1
k2
k4
k3
k1
```
Reducer1得到如下乱序数据
```properties
k1
k2
k1
```
Reducer2得到数据如下
```properties
k4
k3
```
如果想让Reducer上的数据时有序的可以结合`SORT BY`使用(示例如下)或者使用下面我们将要介绍的CLUSTER BY。
```sql
-- 将数据按照部门分发到对应的Reducer上处理
SELECT empno, deptno, sal FROM emp DISTRIBUTE BY deptno SORT BY deptno ASC;
```
### 2.10 CLUSTER BY
如果`SORT BY``DISTRIBUTE BY`指定的是相同字段且SORT BY排序规则是ASC此时可以使用`CLUSTER BY`进行替换,同时`CLUSTER BY`可以保证数据在全局是有序的。
```sql
SELECT empno, deptno, sal FROM emp CLUSTER BY deptno ;
```
## 三、多表联结查询
Hive支持内连接外连接左外连接右外连接笛卡尔连接这和传统数据库中的概念一致。关于以上概念可以参见下图。
需要特别强调JOIN语句的关联条件必须用ON指定不能用WHERE指定否则就会先做笛卡尔积再过滤这会导致你得不到预期的结果(下面的演示会有说明)。
<div align="center"> <img width="600px" src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/sql-join.jpg"/> </div>
### 3.1 INNER JOIN
```sql
-- 查询员工编号为7369的员工的详细信息
SELECT e.*,d.* FROM
emp e JOIN dept d
ON e.deptno = d.deptno
WHERE empno=7369;
--如果是三表或者更多表连接,语法如下
SELECT a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key1)
```
### 3.2 LEFT OUTER JOIN
LEFT OUTER JOIN 和 LEFT JOIN是等价的。
```sql
-- 左连接
SELECT e.*,d.*
FROM emp e LEFT OUTER JOIN dept d
ON e.deptno = d.deptno;
```
### 3.3 RIGHT OUTER JOIN
```sql
--右连接
SELECT e.*,d.*
FROM emp e RIGHT OUTER JOIN dept d
ON e.deptno = d.deptno;
```
执行右连接后由于40号部门下没有任何员工所以此时员工信息为NULL。这个查询可以很好的复述上面提到的——JOIN语句的关联条件必须用ON指定不能用WHERE指定。你可以把ON改成WHERE你会发现无论如何都查不出40号部门这条数据因为笛卡尔运算不会有(NULL, 40)这种情况。
<div align="center"> <img width="700px" src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/hive-right-join.png"/> </div>
### 3.4 FULL OUTER JOIN
```sql
SELECT e.*,d.*
FROM emp e FULL OUTER JOIN dept d
ON e.deptno = d.deptno;
```
### 3.5 LEFT SEMI JOIN
LEFT SEMI JOIN (左半连接)是 IN/EXISTS 子查询的一种更高效的实现。
+ JOIN 子句中右边的表只能在 ON 子句中设置过滤条件;
+ 查询结果只包含左边表的数据所以只能SELECT左表中的列。
```sql
-- 查询在纽约办公的所有员工信息
SELECT emp.*
FROM emp LEFT SEMI JOIN dept
ON emp.deptno = dept.deptno AND dept.loc="NEW YORK";
--上面的语句就等价于
SELECT emp.* FROM emp
WHERE emp.deptno IN (SELECT deptno FROM dept WHERE loc="NEW YORK");
```
### 3.6 JOIN
笛卡尔积连接,这个连接日常的开发中可能很少遇到,且性能消耗会比较大,基于这个原因,如果在严格模式下(hive.mapred.mode = strict)Hive会阻止用户执行此操作。
```sql
SELECT * FROM emp JOIN dept;
```
## 四、JOIN优化
### 4.1 STREAMTABLE
在多表进行联结的时候如果每个ON字句都使用到共同的列如下面的`b.key1`此时Hive会进行优化将多表join在同一个map / reduce作业上进行。同时假定查询的最后一个表如下面的 c 表是最大的一个表在对每行记录进行join操作时它将尝试将其他的表缓存起来然后扫描最后那个表进行计算。因为用户需要保证连续查询的表的大小从左到右是依次增加的。
```sql
`SELECT a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key2)`
```
然后用户并非需要总是把最大的表放在查询语句的最后面Hive提供了`/*+ STREAMTABLE() */`标志,用于标识最大的表,示例如下:
```sql
SELECT /*+ STREAMTABLE(d) */ e.*,d.*
FROM emp e JOIN dept d
ON e.deptno = d.deptno
WHERE job='CLERK';
```
### 4.2 MAPJOIN
如果所有表中只有一张表是小表那么完全可以把这张小表加载到内存中。这时候程序会在map阶段直接拿另外一个表的数据和内存中表数据做匹配由于在map就进行了join操作从而可以省略reduce过程这样效率可以提升很多。Hive中提供了`/*+ MAPJOIN() */`来标记小表,示例如下:
```sql
SELECT /*+ MAPJOIN(d) */ e.*,d.*
FROM emp e JOIN dept d
ON e.deptno = d.deptno
WHERE job='CLERK';
```
## 五、SELECT的其他用途
查看当前数据库:
```sql
SELECT current_database()
```
## 六、本地模式
在上面演示的语句中大多数都会触发MapReduce, 少部分不会触发,比如`select * from emp limit 5`就不会触发MR此时Hive只是简单的读取数据文件中的内容然后格式化后进行输出。在需要执行MapReduce的查询中你会发现执行时间可能会很长这时候你可以选择开启本地模式。
```sql
--本地模式默认关闭,需要手动开启此功能
SET hive.exec.mode.local.auto=true;
```
启用后Hive将分析查询中每个map-reduce作业的大小如果满足以下条件则可以在本地运行它
- 作业的总输入大小低于hive.exec.mode.local.auto.inputbytes.max默认为128MB
- map-tasks的总数小于hive.exec.mode.local.auto.tasks.max默认为4
- 所需的reduce任务总数为1或0。
因为我们测试的数据集很小所以你再次去执行上面涉及MR操作的查询你会发现速度会有显著的提升。
## 参考资料
1. [LanguageManual Select](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+Select)
2. [LanguageManual Joins](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+Joins)
3. [LanguageManual GroupBy](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+GroupBy)
4. [LanguageManual SortBy](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+SortBy)