大数据开发之Hive(查询、分区表和分桶表、函数)

第 6 章:查询

6.1 基本语法及执行顺序

1、查询语句语法

sql 复制代码
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]

2、书写次序和执行次序

顺序 书写次序 书写次序说明 执行次序 执行次序说明
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 4个by distinct 去重
9 order by 4个by distribute by cluster by 4个by
10 limit 限制输出的行数 sort by 4个by
11 union/union all 合并 order by 4个by
12 limit 限制输出的行数
13 union/union all 合并

6.2 基本查询(Select...From)

6.2.1 全表和特定列查询

1、数据准备

分别创建部门和员工外部表,并向表中导入数据。

1)在/opt/module/hive/datas目录下编辑文件dept.txt,添加如下内容。

sql 复制代码
 vim dept.txt
10	行政部	1700
20	财务部	1800
30	教学部	1900
40	销售部	1700

2)在/opt/module/hive/datas目录下编辑文件emp.txt,添加如下内容。

sql 复制代码
vim emp.txt
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

3)上传数据到HDFS

sql 复制代码
dfs -mkdir /user/hive/warehouse/dept;
dfs -mkdir /user/hive/warehouse/emp;
dfs -put /opt/module/hive/datas/dept.txt /user/hive/warehouse/dept;
dfs -put /opt/module/hive/datas/emp.txt /user/hive/warehouse/emp;

4)建表语句,创建外部表

创建部门表dept

sql 复制代码
create external table if not exists dept(
deptno int,--部门编号
dname string, --部门名称
loc int --部门位置
)
row format delimited
fields terminated by '\t';

创建员工表

sql 复制代码
create external table if not exists emp(
empno int, --员工编号
ename string, --员工姓名
job string, --员工岗位(大数据工程师、前端工程师、java工程师)
sal double,--员工薪资
deptno int --部门编号
)
row format delimited fields terminated by '\t';

2、全表查询

sql 复制代码
select * from EMP;
select empno,ename,job,mgr,hiredate,sal,comm,deptno from emp ;

3、选定特定列查询

sql 复制代码
select empno, ename from emp;

注意:

1、SQL语言大小写不敏感

2、SQL可以写在一行或者多行

3、关键字不能被缩写也不能分行

4、各子句一般要分行写

5、使用缩进提高语句的可读性

6.2.2 列别名

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

如:

sql 复制代码
select 
          ename AS name,
          deptno dn 
from emp;

6.2.3 常用函数(set hive.exec.mode.local.auto=true;本地模式)

1、求emp表的总行数(count)

sql 复制代码
select count(*) cnt from emp;

2、求emp表中工资的最大值

sql 复制代码
elect max(sal) max_sal from emp;

3、求emp表中工资的最小值

sql 复制代码
elect min(sal) min_sal from emp;

4、求emp表中工资的总和

sql 复制代码
elect sum(sal) sum_sal from emp;

5、求emp表中工资的平均值

sql 复制代码
select avg(sal) avg_sal from emp;

6.2.4 Limit语句

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

sql 复制代码
select ename, sal from emp limit 5;
select ename, sal from emp limit 2,3;

6.2.5 Where语句

1、实例:查询出薪水大于1000的所有员工

sql 复制代码
select * from emp where sal > 1000;

6.2.6 比较运算符(Between/In/Is Null)

1、下面表中描述了谓词操作符,这些操作符同样可以用于JOIN...ON和HAVING语句中。

操作符 支持的数据类型 描述
A<=>B 基本数据类型 如果A和B都为NULL,则返回TRUE,如果以便为NULL,返回False
A RLIKE B STRING类型 B是基于java的正则表达式,如果A与其匹配,则返回TRUE;反之返回FALSE。匹配使用的是

2、案例实操

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

sql 复制代码
select * from emp where sal =5000;
OK
emp.empno       emp.ename       emp.job emp.mgr emp.hiredate    emp.sal emp.comm        emp.deptno
7839    KING    PRESIDENT       NULL    1981-11-17      5000.0  NULL    10

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

sql 复制代码
select * from emp where sal between 800 and 1100;
OK
emp.empno       emp.ename       emp.job emp.mgr emp.hiredate    emp.sal emp.comm        emp.deptno
7369    SMITH   CLERK   7902    1980-12-17      800.0   NULL    20
7876    ADAMS   CLERK   7788    1987-5-23       1100.0  NULL    20
7900    JAMES   CLERK   7698    1981-12-3       950.0   NULL    30

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

sql 复制代码
select * from emp where job is null;
OK
emp.empno       emp.ename       emp.job emp.mgr emp.hiredate    emp.sal emp.comm        emp.deptno
7369    SMITH   CLERK   7902    1980-12-17      800.0   NULL    20
7566    JONES   MANAGER 7839    1981-4-2        2975.0  NULL    20
7698    BLAKE   MANAGER 7839    1981-5-1        2850.0  NULL    30

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

sql 复制代码
select * from emp where sal IN (1500, 5000);
OK
emp.empno       emp.ename       emp.job emp.mgr emp.hiredate    emp.sal emp.comm        emp.deptno
7839    KING    PRESIDENT       NULL    1981-11-17      5000.0  NULL    10
7844    TURNER  SALESMAN        7698    1981-9-8        1500.0  0.0     30

6.2.8 Like 和 RLike

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

2、选择条件可以包含字符或数字:

1)% -> 代表零个或多个字符

2)_ -> 代表一个字符

3、RLIKE关键字:RLIKE子句是Hive中这个功能的一个扩展,其可以通过java的正则表达式这个更加强大的语言来指定匹配条件。

1)$x -> 代表以x结尾

2)^x -> 代表以x开头

3).* 任意数量字符

4). 一个任意字符

5)*上一个字符可以无限次出现或者不出现

4、实例操作

1)查找名字以"小"开头的员工信息

sql 复制代码
select * from emp where ename LIKE '小%';
select * from emp where ename RLIKE '^小';

2)查找名字以"明"结尾的员工信息

sql 复制代码
select * from emp where ename LIKE '%明';
select * from emp where ename RLIKE '明$';

3)查找名字中带有"明"的员工信息

sql 复制代码
select * from emp where ename  LIKE '%明%';
select * from emp where ename  RLIKE '[明]';

6.3 排序

6.3.1 每个Reduce内部排序(Sort By)

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

2、通过命令设置reduce个数

sql 复制代码
set mapreduce.job.reduces=3;

3、案例实操:

1)根据部门编号降序查看员工信息

sql 复制代码
select * from emp sort by deptno desc;

2)将查询结果导入到文件中

sql 复制代码
insert overwrite local directory '/opt/module/hive/datas/sortby-result'
row format delimited fields terminated by '\t '
select * 
from emp 
sort by deptno desc;

6.3.2 分区(Distribute By)

1、Distribute By

在有些情况下,我们需要控制某个特定行应该在哪个reducer,通常时为了进行后续的聚集操作。distribute by可以实现。distribute by类似MR中的partition(自定义分区),进行分区,结合sort by 使用。

2、案例分析

1)先按照部门编号分区,再按照员工薪水降序排序

sql 复制代码
set mapreduce.job.reduces=3;
insert overwrite local directory '/opt/module/hive/datas/distribute-result'
row format delimited fields terminated by '\t'
select
       ename,
       empno,
       deptno,
       sal 
from emp 
distribute by deptno
sort by sal desc;

注意:

  • distribute by的分区规则是根据分区字段的hash码与reduce的个数进行模除后,余数相同的分到一起。
  • Hive要求DISTRIBUTE BY语句要写在SORT BY语句前面。

6.3.3 Cluster By

1、cluster by:

1)当distribute by和sort by字段相同时,可以使用cluster by方式。

2)cluster by除了具有distribute by的功能外还兼具sort by的功能。

2、案例:查询emp表中的员工信息,并按照部分编号分区排序。

sql 复制代码
select ename,empno,deptno,sal from emp cluster by deptno;
select ename,empno,deptno,sal from emp distribute by deptno sort by deptno;

第 7 章 分区表和分桶表

我们创建一个hive表时,此时在hdfs上就在默认路径上创建了一个以表名字命名的文件夹。Hive表中的数据在hdfs上则是对应文件夹下的所有文件。在查询表中数据时,其实就是将文件下的所有文件进行读取,在海量数据的场景下,这无疑是非常耗时的,并且在实际生产环境中,往往会进行查询过滤。

所以,如何在海量数据的场景下进行高效的查询过滤呢?

7.1 分区表

1、分区表实际上就是对应一个HDFS文件系统上的独立的文件夹。

2、该文件夹下是该分区所有的数据文件。

3、Hive中的分区就是分目录,把一个大的数据集根据业务需求分割成小的数据集。

4、在查询时通过WHERE子句中的表达式选择查询所需要的指定的分区,这样的查询效率会提高很多。

7.1.1 分区表基本操作

1、需要根据日期对日志进行管理,通过部门信息模拟

2、创建分区表语法

sql 复制代码
create table dept_partition(
deptno int, --部门编号
dname string, --部门名称
loc string --部门位置
)
partitioned by (day string)
row format delimited fields terminated by '\t';

注意:分区字段不能是表中已经存在的数据,可以将分区字段看作表的伪列。

3、数据准备

为每个分区准备数据,我们根据日期对日志进行管理,通过部门信息模拟

sql 复制代码
vim dept_20200401.log
10	行政部	1700
20	财务部	1800 
vim dept_20200402.log
30	教学部	1900
40	销售部	1700
vim dept_20200403.log
50	运营部	2000
60	人事部	1900

4、案例:

1)向dept_partition表的分区加载数据

sql 复制代码
load data local inpath '/opt/module/hive/datas/dept_20200401.log' into table dept_partition partition(day='20200401'); 
load data local inpath '/opt/module/hive/datas/dept_20200402.log' into table dept_partition partition(day='20200402');
load data local inpath '/opt/module/hive/datas/dept_20200403.log' into table dept_partition partition(day='20200403');

注意:分区表加载数据时,必须指定分区

2)查询分区表中数据

单分区查询

sql 复制代码
select * from dept_partition where day='20200401';

多分区联合查询(union必走mr效率较低)

sql 复制代码
select * from dept_partition where day='20200401'
              union
              select * from dept_partition where day='20200402'
              union
              select * from dept_partition where day='20200403';
select * from dept_partition where day='20200401' or
                day='20200402' or day='20200403' ;			

5、增加分区

1)添加单个分区

sql 复制代码
alter table dept_partition add partition(day='20200404') ;

2)同时添加多个分区

sql 复制代码
alter table dept_partition add partition(day='20200405') partition(day='20200406');

6、删除分区

1)删除单个分区

sql 复制代码
alter table dept_partition drop partition (day='20200406');

2)同时删除多个分区

sql 复制代码
alter table dept_partition drop partition (day='20200404'), partition(day='20200405');

7、查看分区表结构

sql 复制代码
desc formatted dept_partition;
# Partition Information          
# col_name              data_type               comment             
day                   string    

7.1.2 二级分区

思考:在根据日期分区后,如果一天的日志数据量也很大,如何再将数据拆分?

1、创建二级分区表

sql 复制代码
create table dept_partition2(
       deptno int,
       dname string,
       loc string
)
partitioned by (day string, hour string)
row format delimited fields terminated by '\t';

2、加载数据

1)加载数据到二级分区表中

sql 复制代码
load data local inpath '/opt/module/hive/datas/dept_20200401.log' into table dept_partition2 partition(day='20200401', hour='11');

2)查找分区数据

sql 复制代码
select * from dept_partition2 where day='20200401' and hour='11';

3、让分区表和数据产生关联的三种方式

1)、方式一:上传数据后修复

(1)上传数据

sql 复制代码
 dfs -mkdir -p /user/hive/warehouse/dept_partition2/day=20200401/hour=12;
 dfs -put /opt/module/hive/datas/dept_20200402.log /user/hive/warehouse/dept_partition2/day=20200401/hour=12;

(2)查询数据(查询不到刚上传的数据)

sql 复制代码
select * from dept_partition2 where day='20200401' and hour='12';

(3)执行修复命令

sql 复制代码
msck repair table dept_partition2;

(4)再次查询数据

sql 复制代码
 select * from dept_partition2 where day='20200401' and hour='12';

2)方式二:上传数据后添加分区

(1)上传数据

sql 复制代码
dfs -mkdir -p /user/hive/warehouse/dept_partition2/day=20200401/hour=13;
dfs -put /opt/module/hive/datas/dept_20200403.log /user/hive/warehouse/dept_partition2/day=20200401/hour=13;

(2)执行添加分区

sql 复制代码
alter table dept_partition2 add partition(day='20200401',hour='13');

(3)查询数据

sql 复制代码
select * from dept_partition2 where day='20200401' and hour='14';

3)方式三:创建文件夹后load数据到分区

(1)创建目录

sql 复制代码
dfs -mkdir -p /user/hive/warehouse/mydb.db/dept_partition2/day=20200401/hour=14;

(2)上传数据

sql 复制代码
load data local inpath '/opt/module/hive/datas/dept_20200401.log' into table
 dept_partition2 partition(day='20200401',hour='14');

(3)查询数据

sql 复制代码
select * from dept_partition2 where day='20200401' and hour='14';

7.1.3 动态分区

引言:关系型数据库中,对分区表Insert数据时候,数据库自动会根据分区字段的值,将数据插入到相应的分区中。Hive中也提供类似的操作,即动态分区(Dynamic Partition),只不过,使用Hive的动态分区,需要进行相应的配置。

1、开启动态分区参数设置

1)开启动态分区功能

sql 复制代码
set hive.exec.dynamic.partition=true;

2)设置非严格模式(动态分区的模式,默认strict,表示必须指定至少一个分区为静态分区,nonstrict模式表示允许所有的分区字段都可以使用动态分区)

sql 复制代码
set hive.exec.dynamic.partition.mode=nonstrict

3)在所有执行MR的节点上,最大一共可以创建多少个动态分区。默认1000

sql 复制代码
set hive.exec.max.dynamic.partitions=1000;

4)在每个执行MR的节点上,最大可以创建多少个动态分区

该参数需要根据实际的数据来设定。比如,源数据中包含了一年的数据,即day字段有365个值,那么该参数就需要设置成大于365,如果使用默认100,则会报错。

sql 复制代码
set hive.exec.max.dynamic.partitions.pernode=100;

5)整个MR Job中,最大可以创建多少个HDFS文件。默认100000

sql 复制代码
set hive.exec.max.created.files=100000;

6)当有空分区生成时,是否抛出异常。一般不需要设置。默认false

sql 复制代码
set hive.error.on.empty.partition=false;

2、案例

需求:将dept表中的数据按照地区(loc字段),插入到目标表dept_partition_loc的相应分区中

1)创建部门地区分区表

sql 复制代码
create table dept_partition_dynamic(
       id int,
       name string
)
partitioned by (loc int)
row format delimited fields terminated by '\t';

2)以动态分区的方式向表中插入数据

sql 复制代码
insert into table dept_partition_loc partition(loc) select deptno, dname, loc from dept;
FAILED: SemanticException [Error 10096]: Dynamic partition strict mode requires at least one static partition column. To turn this off set hive.exec.dynamic.partition.mode=nonstrict

set hive.exec.dynamic.partition.mode = nonstrict;

insert into table dept_partition_dynamic partition(loc) select deptno, dname, loc from dept;

3)查看目标分区表的分区情况

sql 复制代码
show partitions dept_partition;
OK
partition
loc=1700
loc=1800
loc=1900

7.2 分桶表

1、分桶表

对于一张表或分区,Hive可以进一步组织成桶,也就是更为细粒度的数据范围划分。分区针对的是数据的存储路径(细分文件夹);分桶针对的是数据文件(按规则多文件放在一起)。

2、案例:创建分桶表

1)创建分桶表

sql 复制代码
create table stu_bucket(id int, name string)
clustered by(id) 
into 4 buckets
row format delimited fields terminated by '\t';

2)查看表结构

sql 复制代码
desc formatted stu_bucket;
Num Buckets:            4     

注意:想要将表创建为4个桶,需要将hive中mapreduce.jog.reduces参数设置为>=4或设置为-1

3)导入数据到分桶表中

sql 复制代码
load data local inpath   '/opt/module/hive/datas/student.txt' into table stu_bucket;

4)查看创建的分桶表中是否分为4个桶

5)查询分桶的数据

sql 复制代码
select * from stu_bucket;

6)分桶规则

Hive的分桶采取对分桶字段的值进行哈希,然后除以桶的个数求余

7)分桶表操作需要注意的事项:

(1)mapreduce.job.reduces=-1,让Job自行决定需要用多少个reduce或者将reduce的个数设置为大于等于分桶表的数量。

(2)从hdfs中load数据到分桶表中,避免本地文件找不到问题

8)insert方式将数据导入分桶表

sql 复制代码
truncate table stu_bucket;(删除表内数据,不删表结构,因此只能删内表)
insert into table stu_bucket select * from student ;

第 8 章:函数

8.1 系统内置函数

1)查看系统自带的函数

sql 复制代码
show functions;

2)显示自带的函数的用法

sql 复制代码
desc function abs;

3)详细显示自带函数的用法

sql 复制代码
desc function extended abs;

8.2 常用内置函数

8.2.1 空字段赋值-NVL(防止空字段参与计算)

1、函数说明

sql 复制代码
desc function extended nvl;

2、解释

NVL 给值为NULL的数据赋值,它的格式是NVL(value,default_value)
功能 如果value为NULL,则NVL函数返回default_value的值,否则返回value的值。如果两个参数都为NULL,则返回NULL

3、案例

1、数据准备

采用员工表

2、查询

1)如果员工的comm为NULL,则用0代替

sql 复制代码
select ename,comm,nvl(comm, 0) comm_0 from emp;

2)如果员工的job为NULL,则用领导id代替

sql 复制代码
select ename, mgr,comm, nvl(job,mgr) comm_mgr from emp;

8.2.2 CASE WHEN ELSE END

1、案例

1)数据准备,在/opt/module/hive/datas目录下创建emp_sex.txt,添加如下内容

sql 复制代码
vim emp_sex.txt
悟空,A,男
大海,A,男
宋宋,B,男
凤姐,A,女
婷姐,B,女
婷婷,B,女

2)创建emp_sex表并导入数据

sql 复制代码
create table emp_sex(
name string, 
dept_id string, 
sex string
) 
row format delimited fields terminated by ",";

load data local inpath '/opt/module/hive/datas/emp_sex.txt' into table emp_sex;

3)需求:求出不同部门男女各多少人。结果如下

sql 复制代码
select 
  dept_id,
  sum(case sex when '男' then 1 else 0 end) man_num,
  sum(case sex when '女' then 1 else 0 end) woman_num
from 
  emp_sex
group by  dept_id;

8.2.3 行转列

1、相关函数说明

1)CONCAT(string A/col,string B/col...)

sql 复制代码
select concat('abc','def') from src limit 1;
'abcdef'

2)CONCAT_WS(separator,str1,str2,...)

sql 复制代码
select concat_ws('.','www',array('facebook','com')) from src limit 1;
'www.facebook.com'

3)COLLECT_SET(col):去重汇总

4)COLLECT_LIST(col):汇总

2、案例

1)需求:把星座和血型一样的人归类到一起。结果如下:

sql 复制代码
射手座,A            大海|凤姐
白羊座,A            孙悟空|猪八戒
白羊座,B            宋宋|苍老师

2)数据准备

sql 复制代码
vim person_info.txt
孙悟空,白羊座,A
大海,射手座,A
宋宋,白羊座,B
猪八戒,白羊座,A
凤姐,射手座,A
苍老师,白羊座,B

3)操作

sql 复制代码
create table person_info(
name string, 
constellation string, 
blood_type string
) 
row format delimited fields terminated by ",";

load data local inpath "/opt/module/hive/datas/person_info.txt" into table person_info;

按需求查询结果

sql 复制代码
SELECT
t1.c_b,
CONCAT_WS("|",collect_set(t1.name))
FROM (
SELECT
NAME ,
CONCAT_WS(',',constellation,blood_type) c_b
FROM person_info
)t1 
GROUP BY t1.c_b

8.2.4 列转行

1、函数说明

1)EXPLODE(col):将hive表的一列中复杂的array或者map结构拆分成多行

2)SPLIT(string str,string regex):按照reget字符串分割str,会返回分割后的字符串数组

sql 复制代码
 SELECT split('oneAtwoBthreeC', '[ABC]') FROM src LIMIT 1;
  ["one", "two", "three"]

3)LATERAL VIEW:对拆分后的数据进行聚合

2、案例

1)需求

sql 复制代码
《疑犯追踪》      悬疑
《疑犯追踪》      动作
《疑犯追踪》      科幻
《疑犯追踪》      剧情
《Lie to me》   悬疑
《Lie to me》   警匪
《Lie to me》   动作
《Lie to me》   心理
《Lie to me》   剧情
《战狼2》        战争
《战狼2》        动作
《战狼2》        灾难

2)原始数据

movie category
《疑犯追踪》 悬疑,动作,科幻,剧情
《Lie to me》 悬疑,警匪,动作,心理,剧情
《战狼2》 战争,动作,灾难

3)操作

sql 复制代码
vim movie_info.txt
《疑犯追踪》	悬疑,动作,科幻,剧情
《Lie to me》	悬疑,警匪,动作,心理,剧情
《战狼2》	战争,动作,灾难

create table movie_info(
    movie string, 
    category string) 
row format delimited
fields terminated by "\t";

load data local inpath "/opt/module/hive/datas/movie_info.txt" into table movie_info;

4)按需求查询数据

sql 复制代码
SELECT movie,category_name 
FROM movie_info 
lateral VIEW
explode(split(category,",")) movie_info_tmp  AS category_name ;

8.2.5 窗口函数(开窗函数)

1、介绍

输入多行数据(一个窗口),为每行数据进行一次计算,返回一个值。灵活运用窗口函数可以解决如去重,排序等。

2、语法

sql 复制代码
Function (arg1 ...) over ([patition by arg1 ...] [order by arg1 ...] [<window_expression>])
Function Over() window_expression
支持的函数 指定分析函数工作的数据窗口大小,窗口会随着行的变化而变化 窗口边界的设置
聚合函数:sum()、max()等 partition by:表示将数据先按字段进行分区 n preceding : 向前n行 n following:向后n行 current row:当前行
排序函数:rank()、row_number()等 Order by:表示将各个分区内的数据按字段进行排序 unbounded preceding:从前面的起点开始 unbounded following:到后面的终点结束
统计比较函数:lead()、lag()等

3、数据准备

1)在/opt/module/hive/datas目录下创建business.txt,添加如下内容

sql 复制代码
vim business.txt
jack,2017-01-01,10
tony,2017-01-02,15
jack,2017-02-03,23
tony,2017-01-04,29
jack,2017-01-05,46
jack,2017-04-06,42
tony,2017-01-07,50
jack,2017-01-08,55
mart,2017-04-08,62
mart,2017-04-09,68
neil,2017-05-10,12
mart,2017-04-11,75
neil,2017-06-12,80
mart,2017-04-13,94

2)创建hive表并导入数据

sql 复制代码
create table business(
name string, 
orderdate string,
cost int
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ',';

load data local inpath "/opt/module/hive/datas/business.txt" into table business;

4、实例

1)需求:查询在2017年4月份购买过的顾客,及总人数

(1)样例

sql 复制代码
name    consume_num
mart    2
jack    2
sql 复制代码
select 
name, 
count(name) over() 
from business 
where subString(orderdate,1,7) = '2017-04'
group by name;

2)需求:查询顾客的购买明细及月购买总额

(1)样例

sql 复制代码
name    orderdate       cost    month_sum
jack    2017-01-05      46      111
jack    2017-01-08      55      111
jack    2017-01-01      10      111
jack    2017-02-03      23      23
jack    2017-04-06      42      42

(2)分析

查询顾客的购买明细,即表中的所有的列,分别以name和orderdate分组,显然group by无法满足我们。这里我们用到over(partition by arg1)指定窗口函数的分区字段,在分区基础上进行窗口分析。

(3)案例

sql 复制代码
select 
name,
orderdate,
cost,
sum(cost) over(partition by name,month(orderdate)) 
from business;
OK
name    orderdate       cost    sum_window_0	
jack    2017-01-05      46      111
jack    2017-01-08      55      111
jack    2017-01-01      10      111
jack    2017-02-03      23      23
jack    2017-04-06      42      42
mart    2017-04-13      94      299
mart    2017-04-11      75      299
mart    2017-04-09      68      299
mart    2017-04-08      62      299
neil    2017-05-10      12      12
neil    2017-06-12      80      80
tony    2017-01-04      29      94
tony    2017-01-02      15      94
tony    2017-01-07      50      94

3)需求:将每个顾客的cost按照日期进行累加

计算表business的消费总额

sql 复制代码
select 
name,
orderdate,
cost, 
sum(cost) over() sample1 
from business;

计算每个人的销售总额

select

name,

orderdate,

cost,

sum(cost) over(partition by name) as sample2

from business;

计算每个人截至到当天的消费总额

sql 复制代码
select 
name,
orderdate,
cost,
sum(cost) over(partition by name order by orderdate) as sample3 from business;

计算每个人截至到今天的消费总额(另一种写法)

sql 复制代码
select 
name,
orderdate,
cost, 
sum(cost) over(partition by name order by orderdate rows between UNBOUNDED PRECEDING and current row ) as sample4
from business;

计算每个人连续两天的消费总额

sql 复制代码
select 
name,
orderdate,
cost, 
sum(cost) over(partition by name order by orderdate rows between 1 PRECEDING and current row ) as sample5
from business;

计算每个人从当前天到最后一天的消费总额

sql 复制代码
select 
name,
orderdate,
cost,
sum(cost) over(partition by name order by orderdate rows between current row and UNBOUNDED FOLLOWING ) as sample6 from business;.

rows必须跟在Order by子句之后,对排序的结果进行限制,使用固定的行数来限制分区中的数量行数量。

4)需求:查看顾客上次的购买时间

(1)样例

sql 复制代码
name    orderdate       cost    last_time
jack    2017-01-01      10      (.....................)
jack    2017-01-05      46      2017-01-01
jack    2017-01-08      55      2017-01-05

(2)函数介绍

sql 复制代码
LAG (scalar_expression[,offset] [,default]) OVER ([query_partition_clause] order_by_clause);

解释:

Lag函数用于统计窗口内往上第n行值,参数scalar_pexpression为列名,参数offset为往上几行,参数default是设置的默认值(当往上第n行为NULL时,取默认值,否则就为NULL)

(3)案例代码

sql 复制代码
select
name,
orderdate,
cost,
lag(orderdate,1,'1900-01-01') over(partition by name order by orderdate ) as last_time
from business;
OK
name    orderdate       cost    last_time
jack    2017-01-01      10      1900-01-01
jack    2017-01-05      46      2017-01-01
jack    2017-01-08      55      2017-01-05
jack    2017-02-03      23      2017-01-08
jack    2017-04-06      42      2017-02-03
mart    2017-04-08      62      1900-01-01

5)需求:查询前20%时间的订单信息

(1)分析

当前表中总共有14行数据,前20%,就是大约前三行,你会觉得很简单,将数据orderdate字段排序取前三即可,但是表中数据量持续变化,前20%的数据是变化的,这里需要使用ntile函数。

(2)函数介绍

Ntile函数,为已排序的行,均分为指定数量的组,组号按顺序排列,返回组号,不支持rows between

(3)案例

sql 复制代码
select
t1.name,
t1.orderdate,
t1.cost
from (
select
name,
orderdate,
cost,
ntile(5) over(order by orderdate) sorted from business
) t1
where t1.sorted = 1;
OK
t.name  t.orderdate     t.cost
jack    2017-01-01      10
tony    2017-01-02      15
tony    2017-01-04      29

8.2.6 Rank

1、函数说明

1)RANK():排序相同时会重复,总数不会变。重复的名次一样但是下一名名次会以前面人数+1来定

2)DENSE_RANK():排序相同时会重复,总数会减少。就是若有重复则最后一名的名词不会和总数相等 即并列

3)ROW_NUMBER():会根据顺序计算,字段相同就按排头字段继续排

2、数据准备

1)数据

sql 复制代码
vim score.txt
孙悟空	语文	87
孙悟空	数学	95
孙悟空	英语	68
大海	语文	94
大海	数学	56
大海	英语	84
宋宋	语文	64
宋宋	数学	86
宋宋	英语	84
婷婷	语文	65
婷婷	数学	85
婷婷	英语	78

2)导入数据

sql 复制代码
create table score(
name string,
subject string, 
score int) 
row format delimited
fields terminated by "\t";

load data local inpath '/opt/module/hive/datas/score.txt' into table score;

3、需求:计算每门学科成绩排名

sql 复制代码
select name,
subject,
score,
rank() over(partition by subject order by score desc) rp,
dense_rank() over(partition by subject order by score desc) drp,
row_number() over(partition by subject order by score desc) rmp
from score;
OK
name    subject score   rp      drp     rmp
孙悟空  数学    95      1       1       1
宋宋    数学    86      2       2       2
婷婷    数学    85      3       3       3
大海    数学    56      4       4       4
宋宋    英语    84      1       1       1
大海    英语    84      1       1       2
婷婷    英语    78      3       2       3
孙悟空  英语    68      4       3       4
大海    语文    94      1       1       1
孙悟空  语文    87      2       2       2
婷婷    语文    65      3       3       3
宋宋    语文    64      4       4       4

8.3 自定义函数

1、内置函数:比如max/min等

2、根据用户自定义函数类别分为以下三种:

1)UDF:一进一出

2)UDAF:聚合函数,多进一出,类似:count/max/min

3)UDTF:炸裂函数,一进多出,类似:explode()

3、编程步骤

1)继承Hive提供的类

2)实现类中的抽象方法

3)在hive的命令行窗口创建函数

4、hive中引入自定义函数步骤

1)添加jar

sql 复制代码
add jar linux_jar_path

2)创建function

sql 复制代码
create [temporary] function [dbname.]function_name AS class_name;

3)在hive的命令行窗口删除函数

sql 复制代码
drop [temporary] function [if exists] [dbname.]function_name;

8.4 自定义UDF函数

1、需求:自定义一个UDF实现计算给定字符串的长度,例如:

sql 复制代码
select my_len("abcd");
ok
4

2、案例

1)创建Maven工程Hive

2)在工程项目的pom.xml文件中导入依赖

hive-exec

3)创建一个类

sql 复制代码
package com.atguigu.hive;

import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentLengthException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentTypeException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDF;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;

/**
 * 自定义UDF函数,需要继承GenericUDF类
 * 需求: 计算指定字符串的长度
 */
public class MyStringLength extends GenericUDF {
    /**
     *
     * @param arguments 输入参数类型的鉴别器对象
     * @return 返回值类型的鉴别器对象
     * @throws UDFArgumentException
     */
    @Override
    public ObjectInspector initialize(ObjectInspector[] arguments) throws UDFArgumentException {
        // 判断输入参数的个数
        if(arguments.length !=1){
            throw new UDFArgumentLengthException("Input Args Length Error!!!");
        }
        // 判断输入参数的类型
        if(!arguments[0].getCategory().equals(ObjectInspector.Category.PRIMITIVE)){
            throw new UDFArgumentTypeException(0,"Input Args Type Error!!!");
        }
        //函数本身返回值为int,需要返回int类型的鉴别器对象
        return PrimitiveObjectInspectorFactory.javaIntObjectInspector;
    }

    /**
     * 函数的逻辑处理
     * @param arguments 输入的参数
     * @return 返回值
     * @throws HiveException
     */
    @Override
    public Object evaluate(DeferredObject[] arguments) throws HiveException {
       if(arguments[0].get() == null){
           return 0 ;
       }
       return arguments[0].get().toString().length();
    }

    @Override
    public String getDisplayString(String[] children) {
        return "";
    }
}

4)打包jar包上传到服务器/opt/module/hive/datas/myudf.jar

5)将jar包添加到hive的classpath

sql 复制代码
add jar /opt/module/hive/datas/myudf.jar;

8.5 创建临时函数

1、创建临时函数与开发好的java class关联

sql 复制代码
create temporary function my_len as "com.atguigu.hive. MyStringLength";

2、在hql中使用自定义的函数

sql 复制代码
select ename,my_len(ename) ename_len from emp;
OK
ename   _c1
fanfan  6
SMITH   5
ALLEN   5
WARD    4
JONES   5
MARTIN  6
BLAKE   5
CLARK   5
SCOTT   5
KING    4
TURNER  6
ADAMS   5
JAMES   5
FORD    4
MILLER  6

注意:临时函数只跟会话有关系,跟库没有关系,只有创建临时函数的会话不断,在当前会话下,任意一个库都可以使用,其他会话全部不能使用。

8.6 创建永久函数

注意:因为add jar 的方式本身也是临时生效,所以在创建永久函数的时候,需要执行路径

sql 复制代码
create function my_len2 
as "com.atguigu.hive.udf.MyUDF" 
using jar "hdfs://hadoop102:8020/udf/myudf.jar";

即可在hql中使用自定义的永久函数

sql 复制代码
select 
    ename,
    my_len2(ename) ename_len 
from emp;

删除永久函数

sql 复制代码
drop function my_len2;

注意:永久函数跟会话没有关系,创建函数的会话断了以后,其他会话也可以使用。

相关推荐
szxinmai主板定制专家2 小时前
【国产NI替代】基于FPGA的32通道(24bits)高精度终端采集核心板卡
大数据·人工智能·fpga开发
TGB-Earnest3 小时前
【py脚本+logstash+es实现自动化检测工具】
大数据·elasticsearch·自动化
大圣数据星球5 小时前
Fluss 写入数据湖实战
大数据·设计模式·flink
suweijie7685 小时前
SpringCloudAlibaba | Sentinel从基础到进阶
java·大数据·sentinel
Data跳动11 小时前
Spark内存都消耗在哪里了?
大数据·分布式·spark
woshiabc11112 小时前
windows安装Elasticsearch及增删改查操作
大数据·elasticsearch·搜索引擎
lucky_syq12 小时前
Saprk和Flink的区别
大数据·flink
lucky_syq12 小时前
流式处理,为什么Flink比Spark Streaming好?
大数据·flink·spark
袋鼠云数栈12 小时前
深入浅出Flink CEP丨如何通过Flink SQL作业动态更新Flink CEP作业
大数据