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

13 KiB
Raw Blame History

Hive数据查询详解

一、数据准备
二、单表查询
        2.1 SELECT
        2.2 WHERE
        2.3 DISTINCT
        2.4 分区查询
        2.5 LIMIT
        2.6 GROUP BY
        2.7 ORDER AND SORT
        2.8 HAVING
        2.9 DISTRIBUTE BY
        2.10 CLUSTER BY
三、多表联结查询
        3.1 INNER JOIN
        3.2 LEFT OUTER JOIN
        3.3 RIGHT OUTER JOIN
        3.4 FULL OUTER JOIN
        3.5 LEFT SEMI JOIN
        3.6 JOIN
四、JOIN优化
        4.1 STREAMTABLE
        4.2 MAPJOIN
五、SELECT的其他用途
六、本地模式

一、数据准备

为了演示查询操作,这里需要预先创建两张表,并加载数据。

用例表结构参考自Oracle内置练习表——emp和dept数据文件emp.txt和dept.txt可以从本仓库的resources目录下载。

1.1 员工表

 -- 建表语句
 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 部门表

 -- 建表语句
 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 分区表

这里需要额外创建一张分区表,主要是为了演示分区查询:

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

-- 查询表中全部数据
SELECT * FROM emp;

2.2 WHERE

-- 查询10号部门中员工编号大于 7782 的员工信息 
SELECT * FROM emp WHERE empno > 7782 AND deptno = 10;

2.3 DISTINCT

Hive支持使用DISTINCT关键字去重。

-- 查询所有工作类型
SELECT DISTINCT job FROM emp;

2.4 分区查询

分区查询(Partition Based Queries),可以指定某个分区或者分区范围。

-- 查询分区表中部门编号在[20,40]之间的员工
SELECT emp_ptn.* FROM emp_ptn
WHERE emp_ptn.deptno >= 20 AND emp_ptn.deptno <= 40;

2.5 LIMIT

-- 查询薪资最高的5名员工
SELECT * FROM emp ORDER BY sal DESC LIMIT 5;

2.6 GROUP BY

Hive支持使用GROUP BY进行分组聚合操作。

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 ,也就是非严格模式。

-- 查询员工工资,结果按照部门升序,按照工资降序排列
SELECT empno, deptno, sal FROM emp ORDER BY deptno ASC, sal DESC;

2.8 HAVING

可以使用HAVING对分组数据进行过滤。

-- 查询工资总和大于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上进行处理

k1
k2
k4
k3
k1

Reducer1得到如下乱序数据

k1
k2
k1

Reducer2得到数据如下

k4
k3

如果想让Reducer上的数据时有序的可以结合SORT BY使用(示例如下)或者使用下面我们将要介绍的CLUSTER BY。

-- 将数据按照部门分发到对应的Reducer上处理
SELECT empno, deptno, sal FROM emp DISTRIBUTE BY deptno SORT BY deptno ASC;

2.10 CLUSTER BY

如果SORT BYDISTRIBUTE BY指定的是相同字段且SORT BY排序规则是ASC此时可以使用CLUSTER BY进行替换,同时CLUSTER BY可以保证数据在全局是有序的。

SELECT empno, deptno, sal FROM emp CLUSTER  BY deptno ;

三、多表联结查询

Hive支持内连接外连接左外连接右外连接笛卡尔连接这和传统数据库中的概念一致。关于以上概念可以参见下图。

需要特别强调JOIN语句的关联条件必须用ON指定不能用WHERE指定否则就会先做笛卡尔积再过滤这会导致你得不到预期的结果(下面的演示会有说明)。

3.1 INNER JOIN

-- 查询员工编号为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是等价的。

-- 左连接
SELECT e.*,d.*
FROM emp e LEFT OUTER  JOIN  dept d
ON e.deptno = d.deptno;

3.3 RIGHT OUTER JOIN

--右连接
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)这种情况。

3.4 FULL OUTER JOIN

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左表中的列。
-- 查询在纽约办公的所有员工信息
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会阻止用户执行此操作。

SELECT * FROM emp JOIN dept;

四、JOIN优化

4.1 STREAMTABLE

在多表进行联结的时候如果每个ON字句都使用到共同的列如下面的b.key1此时Hive会进行优化将多表join在同一个map / reduce作业上进行。同时假定查询的最后一个表如下面的 c 表是最大的一个表在对每行记录进行join操作时它将尝试将其他的表缓存起来然后扫描最后那个表进行计算。因为用户需要保证连续查询的表的大小从左到右是依次增加的。

`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() */标志,用于标识最大的表,示例如下:

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() */来标记小表,示例如下:

SELECT /*+ MAPJOIN(d) */ e.*,d.* 
FROM emp e JOIN dept d
ON e.deptno = d.deptno
WHERE job='CLERK';

五、SELECT的其他用途

查看当前数据库:

SELECT current_database()

六、本地模式

在上面演示的语句中大多数都会触发MapReduce, 少部分不会触发,比如select * from emp limit 5就不会触发MR此时Hive只是简单的读取数据文件中的内容然后格式化后进行输出。在需要执行MapReduce的查询中你会发现执行时间可能会很长这时候你可以选择开启本地模式。

--本地模式默认关闭,需要手动开启此功能
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

  2. LanguageManual Joins

  3. LanguageManual GroupBy

  4. LanguageManual SortBy