第 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;
注意:永久函数跟会话没有关系,创建函数的会话断了以后,其他会话也可以使用。