0%

Hadoop-18-Hive3查询操作

基础语法及执行顺序

查询语句语法

1
2
3
4
5
6
7
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]

书写次序和执行次序

顺序 书写次序 书写次序说明 执行次序 执行次序说明
1 select 查询 from 先执行表与表直接的关系
2 from 先执行表与表直接的关系 on
3 join on join
4 where where 过滤
5 group by 分组 group by 分组
6 having 分组后再过滤 having 分组后再过滤
7 distribute by
cluster by
4个by select 查询
8 sort by distinct 去重
9 order by distribute by
cluster by
4个by
10 limit 限制输出的行数 sort by
11 union/union all 合并 order by
12 limit 限制输出的行数
13 union /union all 合并

基本查询(Select…From)

全表和特定列查询

首先数据准备,在/opt/module/hive/datas目录下增加dept.txtemp.txt文件。

  1. dept.txt文件内容如下:

    1
    2
    3
    4
    10	行政部	1700
    20 财务部 1800
    30 教学部 1900
    40 销售部 1700
  2. emp.txt文件内容如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    7369	张三	研发	800.00	30
    7499 李四 财务 1600.00 20
    7521 王五 行政 1250.00 10
    7566 赵六 销售 2975.00 40
    7654 侯七 研发 1250.00 30
    7698 马八 研发 2850.00 30
    7782 金九 \N 2450.0 30
    7788 银十 行政 3000.00 10
    7839 小芳 销售 5000.00 40
    7844 小明 销售 1500.00 40
    7876 小李 行政 1100.00 10
    7900 小元 讲师 950.00 30
    7902 小海 行政 3000.00 10
    7934 小红明 讲师 1300.00 30

然后创建表deptemp,并加载数据如下所示:

1
2
3
4
5
6
7
8
9
create external table if not exists dept(
deptno int,
dname string,
loc int
)
row format delimited
fields terminated by '\t';

load data local inpath '/opt/module/hive-3.1.2/datas/dept.txt' into table dept;
1
2
3
4
5
6
7
8
9
10
11
create external table if not exists emp(
empno int,
ename string,
job string,
sal double,
deptno int
)
row format delimited
fields terminated by '\t';

load data local inpath '/opt/module/hive-3.1.2/datas/emp.txt' into table emp;

全表查询

1
select * from dept;

image-20230808194757988

选择特定列查询

1
select dname, loc from dept;

image-20230808194833610

注意:

  1. SQL 语言大小写不敏感。
  2. SQL可以写在一行或者多行
  3. 关键字不能被缩写也不能分行
  4. 各子句一般要分行写。
  5. 使用缩进提高语句的可读性。

列别名

列别名:为列指定一个别名

优点: 便于计算

实现: 紧跟列名,也可以在列名和别名之间加入关键字AS

  • 查询税后收入(税后收入=总收入*80%)

    1
    select dname, loc * 0.8 from dept;

    image-20230808195111243

    这个是直接查询的结果,结果中可以看到查询到的税后收入列明为_c1,这个列名没有什么实际意义,可以手动给其取一个别名,这样在打印的时候就会显示对应的别名。

    1
    select dname, loc * 0.8 as shuihou from dept;

    image-20230808195239365

这里的as参数也可以省略。

算数运算符

运算符 描述
A+B AB相加
A-B A减去B
A*B AB相乘
A/B A除以B
A%B AB取余
A&B AB按位取与
`A B` AB按位取或
A^B AB按位取异或
~A A按位取反

案例演示见前一节给的案例,计算税后收入。

常用函数

函数名称 函数意义
count 求总行数
max 求最大值
min 求最小值
sum 求总和
avg 求平均值
  1. emp表的总行数

    1
    select count(1) from emp;

    这个时候由于文件数不为1,但是行数确为1,此时需要经过MR才能计算总行数。

    image-20230808200015457

  2. emp表的工资最大值

    1
    select max(sal) from emp;

    此时仍然需要经过MR才可以计算出结果。

    image-20230808200130447

    为了让简单任务计算快一点,可以设置不让任务经过YARN集群,采用本地模式执行。

    使用以下命令设置参数即可。

    1
    set hive.exec.mode.local.auto=true;

    此时再执行相关代码就会再本地模式中运行MR,不需要走YARN使用集群计算。

    但是要注意,这种情况下仅适用于数据量不大,并且计算难度小的任务。

  3. emp表工资最小值。

    1
    select min(sal) from emp;

    image-20230808200544322

    设置完本地模式后,此时MR就不使用YARN去执行了,直接在本地执行,大大节省了等待的时间。

  4. emp表工资总和。

    1
    select sum(sal) from emp;

    image-20230808200714817

  5. emp表工资平均值。

    1
    select avg(sal) from emp;

    image-20230808200743886

Limit语句

一般的查询会返回多行数据,在成产环境中,通常使用LIMIT子句用于限制返回的行数。

  • 返回指定的行数
1
select * from emp limit 5;

image-20230808211541526

  • 指定偏移量和显示的行数
1
select * from emp limit 3, 5;

image-20230808211647158

where语句

  1. 使用WHERE子句,将不满足条件的行过滤掉。
  2. WHERE子句紧随FROM子句。
  • 查询出薪水大于2000的所有员工姓名。

    1
    select ename, sal from emp where sal > 2000;

    image-20230808211840952

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

因为where的运算次序比select的运算次序高,在执行where时别名还未生效。

比较运算符

下面表中描述了谓词操作符,这些操作符同样可以用于JOIN…ONHAVING语句中。

操作符 支持的数据类型 描述
A=B 基本数据类型 如果A等于B则返回TRUE,反之返回FALSE
A<=>B 基本数据类型 如果AB都为NULL,则返回TRUE, 如果一边为NULL,返回False
A<>B, A!=B 基本数据类型 A或者BNULL则返回NULL
如果A不等于B,则返回TRUE,反之返回FALSE
A<B 基本数据类型 A或者BNULL,则返回NULL
如果A小于B,则返回TRUE,反之返回FALSE
A<=B 基本数据类型 A或者BNULL,则返回NULL
如果A小于等于B,则返回TRUE,反之返回FALSE
A>B 基本数据类型 A或者BNULL,则返回NULL
如果A大于B,则返回TRUE,反之返回FALSE
A>=B 基本数据类型 A或者BNULL,则返回NULL
如果A大于等于B,则返回TRUE,反之返回FALSE
A [NOT] BETWEEN B AND C 基本数据类型 如果AB或者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 STRING类型 B是基于java的正则表达式,如果A与其匹配,则返回TRUE; 反之返回FALSE。 匹配使用的是JDK中的正则表达式接口实现的,因为正则也依据其中的规则。 例如,正则表达式必须和整个字符串A相匹配,而不是只需与其子字符串匹配。

案例实操:

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

    1
    select ename, job from emp where sal = 5000;

    image-20230808212758461

  2. 查询工资在5001000的员工信息

    1
    select ename, job from emp where sal between 500 and 1000;

    image-20230808212909964

  3. 查询job为空的所有员工信息

    1
    select ename, job from emp where job is null;

    image-20230808212943883

  4. 查询工资是15005000的员工信息

    1
    select ename, job from emp where sal in (1500, 5000);

    image-20230808213030399

like和rlike

  1. like关键字:使用LIKE运算选择类似的值

    • 选择条件可以包含字符或数字
    • % -->代表零个或多个字符(任意个字符)。
  • _ -->代表一个字符。
  1. rlike关键字:RLIKE子句是Hive中这个功能的一个扩展,其可以通过Java的正则表达式这个更强大的语言来指定匹配条件。

案例实操:

  1. 查找名字以“小”开头的员工信息

    1
    select ename, job from emp where ename like "小%";

    image-20230808213610819

    1
    select ename, job from emp where ename rlike "^小";

    image-20230808213729934

  2. 查找名字以“明”结尾的员工信息

    1
    select ename, job from emp where ename like "%明";

    image-20230808213809403

    1
    select ename, job from emp where ename rlike "明$";

    image-20230808213851612

  3. 查找名字中带有“明”的员工信息

    1
    select ename, job from emp where ename like "%明%";

    image-20230808213946981

    1
    select ename, job from emp where ename rlike "明+";

    image-20230808214054087

逻辑运算符(And/Or/Not)

操作符 含义
AND 逻辑并
OR 逻辑或
NOT 逻辑否

案例实操:

  1. 查询薪水大于1000,部门是30的员工信息。

    1
    select ename, job from emp where sal > 1000 and deptno = 30;

    image-20230808214225990

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

    1
    select ename, job, deptno, sal from emp where sal > 1000 or deptno = 30;

    image-20230808214348115

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

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

    image-20230808214428478

分组查询

Group By语句

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

案例实操:

  1. 计算emp表每个部门的平均工资

    1
    select deptno, avg(sal) sal_avg from emp group by deptno;

    image-20230808214754521

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

    1
    select deptno, job, max(sal) sal_max from emp group by deptno, job;

    image-20230808214851912

Having语句

havingwhere不同点:

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

案例实操:

  1. 求每个部门的平均工资

    1
    select deptno, avg(sal) sal_avg from emp group by deptno;

    image-20230808215046583

  2. 求每个部门的平均薪水大于2000的部门

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

    image-20230808215501540

    注意:在having中可以使用字段别名,因为where是先判断数据是否符合条件,符合条件的数据再入内存中然后生成别名,所以别名是where判断条件后才生成的,这 也是where不能使用别名的原因。

    having是先从磁盘中读取数据到内存中,然后才进行判断,这个时候别名已经生成了,所以having可以使用别名。

Join语句

image-20230808220441597

常见的连接方式有6种,后面依次举例说明。

要演示该部分内容,首先需要对前面的数据进行修改,每个表格插入几条特有数据。

1
insert into table emp values(1001, '王麻子', '研发', 4000, 50);
1
insert into table dept values(60, '总经办', 1900);

内连接

image-20230809092727239

1
2
3
4
5
6
7
8
select
e.deptno,
e.job,
e.sal,
d.dname,
d.loc
from emp as e
join dept d on e.deptno = d.deptno;

结果如下所示,这里我们采用IDEA方式运行,结果更直观。

image-20230809092649938

左连接

image-20230809092742728

1
2
3
4
5
6
7
8
9
10
-- 左连接
select
e.ename,
e.deptno,
e.job,
e.sal,
d.dname,
d.loc
from emp as e
left join dept d on e.deptno = d.deptno;

image-20230809092516324

可以看到王麻子此时也被包含到了结果中。

思考:如何只保存左表所特有的数据。

image-20230809092823721

1
2
3
4
5
6
7
8
9
10
select
e.ename,
e.deptno,
e.job,
e.sal,
d.dname,
d.loc
from emp as e
left join dept d on e.deptno = d.deptno
where d.dname is null;

此时结果中只有王麻子。

image-20230809093207092

右连接

image-20230809093322531

1
2
3
4
5
6
7
8
9
10
-- 右连接
select
e.ename,
e.deptno,
e.job,
e.sal,
d.dname,
d.loc
from emp as e
right join dept d on e.deptno = d.deptno;

image-20230809093254217

同样思考一下,如何只保存右表中所特有的数据。

image-20230809093528856

1
2
3
4
5
6
7
8
9
10
11
12
-- 右连接,只保存右表中所特有的数据
select
e.ename,
e.deptno,
e.job,
e.sal,
d.dname,
d.loc
from emp as e
right join dept d
on e.deptno = d.deptno
where e.ename is null;

此时结果中只有总经办

image-20230809093456391

外连接

image-20230809093621808

1
2
3
4
5
6
7
8
9
10
-- 外连接
select
e.ename,
e.deptno,
e.job,
e.sal,
d.dname,
d.loc
from emp as e
full outer join dept d on e.deptno = d.deptno;

image-20230809093712086

思考:如何只保存两个表中所特有的数据。

image-20230809093820136

1
2
3
4
5
6
7
8
9
10
11
12
-- 外连接, 只保存两个表中所特有的数据。
select
e.ename,
e.deptno,
e.job,
e.sal,
d.dname,
d.loc
from emp as e
full outer join dept d
on e.deptno = d.deptno
where e.ename is null or d.dname is null;

image-20230809093902759

多表连接

为了演示这个内容,还需要再创建一个表location.txt,这个表的内容是具体的地点编号对应的地点,内容如下:

1
2
3
4
1700	武汉市洪山区
1800 武汉市汉阳区
1900 武汉市江夏区
2000 武汉市汉口区

创建表如下所示:

1
2
3
4
5
6
7
8
9
10
11
-- 创建地点表
create external table if not exists location(
loc int,
loc_string string
)
row format delimited
fields terminated by '\t'
lines terminated by '\n';

-- 加载数据
load data local inpath '/opt/module/hive-3.1.2/datas/location.txt' into table location;

image-20230809100105334

  • 查询每个员工姓名、部门名称及部门所在地
1
2
3
4
5
6
7
8
-- 查询每个员工姓名、部门名称及部门所在地
select
e.ename,
d.dname,
l.loc_name
from emp e
left join dept d on e.deptno = d.deptno
left join location l on d.loc = l.loc;

image-20230809100300831

笛卡尔积

笛卡尔积会在下面条件下产生

  1. 省略连接条件
  2. 连接条件无效
  3. 所有表中的所有行互相连接

案例实操:

1
2
select empno, dname
from emp, dept;

image-20230809100501106

此时一共获取了75条数据。

排序

全局排序(Order By)

Order By:全局排序,只有一个Reducer

顺序:① ASC(ascend): 升序(默认);② DESC(descend): 降序

位置:ORDER BY子句在SELECT语句的结尾

案例实操:

  1. 查询员工信息按工资升序排列

    1
    select * from emp order by sal asc;

    image-20230809100801719

  2. 查询员工信息按工资降序排列

    1
    select * from emp order by sal desc;

    image-20230809100854982

  3. 按照员工税后薪水排序

    1
    select ename, sal*0.8 shuihou from emp order by shuihou;

    image-20230809101023239

  4. 按照部门升序和工资降序排序

    1
    select deptno, sal from emp order by deptno asc, sal desc;

    image-20230809101154442

注意:使用order by对数据排序时只有一个reduce,面对海量数据排序时,一个reduce效率太低,并且大多数场景下,并不需要全局排序,一般都是筛选出top5top10等等。

例如,我们手动把reduces数设置为3

1
2
>-- 设置reduce为3
>set mapreduce.job.reduces=3;

注意:由于前面我们设置过本地模式运行,所以这里需要将其设置回来使用YARN去执行。可以手动执行命令set hive.exec.mode.local.auto=false;切换,也可以简单点直接重启Hive客户端。

再次执行排序操作

1
>select * from emp order by sal desc;

image-20230809103436973

可以看到,即使我们手动将reduce的值设置为3,最终在执行排序任务时,仍然时只有一个reducer

每个Reduce内部排序(Sort By)

Sort by:在每个Reducer内部进行排序,对全局结果集来说不是有序。sort by为每个reducer产生一个排序文件,每个Reducer内部进行排序,对全局结果来说不是排序。

1
2
3
4
5
-- 设置reducer个数为3
set mapreduce.job.reduces=3;

-- 排序
select * from emp sort by sal desc;

image-20230809103742796

可以看到此时排序的结果并不是完全有序的,将结果导入文件中。

1
2
3
4
5
insert overwrite local directory "/opt/module/hive-3.1.2/datas/export"
row format delimited
fields terminated by '\t'
lines terminated by '\n'
select * from emp sort by sal desc;

此时在export中就会生成三个文件,这三个文件分别对应于3reducer

image-20230809104134412

依次查看每个文件内容:

image-20230809104259775

image-20230809104311783

image-20230809104319962

可以看到这三个文件内容是排序的结果。

通过观察结果我们发现,进入reduce里面的数据是随机的,如何才能控制相应字段进入到同一个reduce中呢?

分区(Distribute By)

在有些情况下,我们需要控制某个特定行应该到哪个reducer,通常时为了进行后续的聚集操作。

distribute by可以实现。distribute by类似MRpartition(自定义分区),进行分区,结合sort by使用。

对于distribute by进行测试,一定要分配多reduce进行处理,否则无法看到distribute by的效果。

1
2
3
4
5
6
7
8
set mapreduce.job.reduces=3;
insert overwrite local directory "/opt/module/hive-3.1.2/datas/export"
row format delimited
fields terminated by '\t'
lines terminated by '\n'
select * from emp
distribute by deptno
sort by sal desc;

此时再次查看export目录下的文件内容。

image-20230809105823088

image-20230809105833749

image-20230809105841528

可以看到此时已经按照部门进行分区了。

注意:

  • distribute by的分区规则是根据分区字段的hash码与reduce的个数进行模除后,余数相同的分到一起。

    第一个表:30%3=0

    第二个表:40%3=1;10%3=1

    第三个表:50%3=2;20%3=2

  • Hive要求DISTRIBUTE BY语句要写在SORT BY语句之前。

  • 测试完毕后,记得将mapreduce.job.reduces的值恢复到-1,否则下面的分区和分桶测试就会报错。

Cluster By

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

查询emp表中的员工姓名、员工编号、部门编号、薪资,并按照部门编号分区排序。

1
2
3
4
select ename,empno,deptno,sal 
from emp
distribute by deptno
sort by deptno;

image-20230809110536409

上面写法与以下写法等价。

1
select ename,empno,deptno,sal from emp cluster by deptno;

image-20230809110727779

-------------本文结束感谢您的阅读-------------