文章目录
- 语法及注意事项
- 基本查询(where、gruop by、join)
- 排序
- 函数
- 系统内置函数
- 窗口函数
- 自定义函数
语法及注意事项
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 [offset,] rows]
SQL注意事项
1)SQL语句对大小写不敏感;
2)SQL语句可以写一行,也可多行;
3)关键字不能缩写,也不能分行;
4)各子句一般要分行;
5)使用缩进格式,提高SQL语句的可读性
基本查询(where、gruop by、join)
全表查询
select * from emp;
选择特定列查询
select ename, sal, comm from emp;
使用函数&别名
select count(*) as empcount from emp;
//count(colname) 按字段进行count,不统计NULL
select sum(sal) from emp;
select max(sal) from emp;
select min(sal) from emp;
select avg(sal) from emp;
使用limit子句限制返回的行数
select * from emp limit 3;
where过滤条件
select * from emp where sal > 2000;
运算符相关
比较运算符,null参与运算
select null=null;
select null==null;
select null<=>null;
使用 is null 判空
select * from emp where comm is null;
使用 in
select * from emp where deptno in (20, 30);
使用 between ... and ...
select * from emp where sal between 1000 and 2000;
使用 like
select ename, sal from emp where ename like '%L%';
使用 rlike-正则表达式,名字以A或S开头
select ename, sal from emp where ename rlike '^(A|S).*';
分组 group by
计算emp表每个部门的平均工资
select deptno, avg(sal)
from emp
group by deptno;
计算emp每个部门中每个岗位的最高薪水
select deptno, job, max(sal)
from emp
group by deptno, job;
求每个部门的平均薪水大于2000的部门 group by + having
select deptno, avg(sal)
from emp
group by deptno
having avg(sal) > 2000;
连接
内连接
select * from u1 join u2 on u1.id = u2.id;
左外连接
select * from u1 left join u2 on u1.id = u2.id;
右外连接
select * from u1 right join u2 on u1.id = u2.id;
全外连接
select * from u1 full join u2 on u1.id = u2.id;
多表连接 查询老师对应的课程,以及对应的分数,对应的学生
select *
from techer t left join course c on t.t_id = c.t_id
left join score s on s.c_id = c.c_id
left join student stu on s.s_id = stu.s_id;
注意:
连表时,Hive总是按照从左到右的顺序执行,Hive会对每对 JOIN 连接对象启动一个MapReduce 任务;
上面多表连接的案例中会首先启动一个 MapReduce job 对表 t 和表 c 进行连接操作;
然后再启动一个 MapReduce job 将第一个 MapReduce job 的输出和表 s 进行连接操作;
然后再继续直到全部操作。
排序
全局排序 order by
语法同sql,order by 子句出现在select语句的结尾;order by子句对最终的结果进行排序;
默认使用升序(ASC);可以使用DESC,跟在字段名之后表示降序;
ORDER BY执行全局排序,只有一个reduce
1)普通排序
select * from emp order by deptno;
2)按别名排序
select empno, ename, job, mgr, sal + nvl(comm, 0) salcomm,deptno
from emp
order by salcomm desc;
3)多列排序
select empno, ename, job, mgr, sal + nvl(comm, 0) salcomm,deptno
from emp
order by deptno, salcomm desc;
//排序字段要出现在select子句中
每个MR内部排序sort by
对于大规模数据而言order by效率低; 在很多业务场景,我们并不需要全局有序的数据,此时可以使用sort by;
sort by为每个reduce产生一个排序文件,在reduce内部进行排序,得到局部有序的结果;
设置reduce个数
set mapreduce.job.reduces=2;
按照工资降序查看员工信息
select * from emp sort by sal desc;
将查询结果导入到文件中(按照工资降序)。生成两个输出文件,每个文件内部数据按工资降序排列
insert overwrite local directory '/home/hadoop/output/sortsal'
select * from emp sort by sal desc;
分区排序 distriubute
clike distribute by 将特定的行发送到特定的reducer中,便于后继的聚合 与 排序操作 distribute;
by 类似于MR中的分区操作,可以结合sort by操作,使分区数据有序 distribute by 要写在sort by之前
启动2个reducer task
set mapreduce.job.reduces=2;
//先按 deptno 分区,在分区内按 sal+comm 排序;
将结果输出到文件,观察输出结果
insert overwrite local directory '/home/hadoop/output/distBy'
select empno, ename, job, deptno, sal + nvl(comm, 0) salcomm
from emp
distribute by deptno
sort by salcomm desc;
//上例中,数据被分到了统一区,看不出分区的结果
将数据分到3个区中,每个分区都有数据
set mapreduce.job.reduces=3;
insert overwrite local directory '/home/hadoop/output/distBy1'
select empno, ename, job, deptno, sal + nvl(comm, 0) salcomm
from emp
distribute by deptno
sort by salcomm desc;
cluster by
当distribute by 与 sort by是同一个字段时,可使用cluster by简化语法;
cluster by 只能是升序,不能指定排序规则;
语法上是等价的
select * from emp distribute by deptno sort by deptno;
select * from emp cluster by deptno;
函数
系统内置函数
查看系统函数
查看系统自带函数
show functions;
显示自带函数的用法
desc function upper;
desc function extended upper;
日期函数
当前日期
select current_date;
select unix_timestamp();(荒废;毫秒)
select current_timestamp();
//建议使用current_timestamp,有没有括号都可以
时间戳转日期
select from_unixtime(1505456567);
select from_unixtime(1505456567, 'yyyyMMdd');
select from_unixtime(1505456567, 'yyyy-MM-dd HH:mm:ss');
日期转时间戳
select unix_timestamp('2019-09-15 14:23:00');
计算时间差
select datediff('2020-04-18','2019-11-21');
select datediff('2019-11-21', '2020-04-18');(负)
查询当月第几天
select dayofmonth(current_date);
计算月末
select last_day(current_date);
当月第1天
select date_sub(current_date,dayofmonth(current_date)-1)
下个月第1天
select add_months(date_sub(current_date,
dayofmonth(current_date)-1), 1)
字符串转时间(字符串必须为:yyyy-MM-dd格式)
select to_date('2020-01-01');
select to_date('2020-01-01 12:12:12');
日期、时间戳、字符串类型格式化输出标准时间格式
select date_format(current_timestamp(), 'yyyy-MM-dd HH:mm:ss');
select date_format(current_date(), 'yyyyMMdd');
select date_format('2020-06-01', 'yyyy-MM-dd HH:mm:ss');
计算emp表中,每个人的工龄
select *, round(datediff(current_date,hiredate)/365,1)
workingyears from emp;
字符串函数
转小写-lower
select lower("HELLO WORLD");
转大写-upper
select lower(ename), ename from emp;
求字符串长度-length
select length(ename), ename from emp;
字符串拼接- concat / ||
select empno || " " ||ename || " " || idname from emp;
select concat(empno, " " ,ename) idname from emp;
指定分隔符-concat_ws(separator, [string | array(string)]+)
SELECT concat_ws('.', 'www', array('baidu', 'com'));
select concat_ws(" ", ename, job) from emp;
求子串-substr
SELECT substr('www.baidu.com', 5);
SELECT substr('www.baidu.com', -5);最后5个
SELECT substr('www.baidu.com', 5, 5);截取5个
字符串切分-split,注意 '.' 要转义
select split("www.baidu.com", "\\.");
数学函数
四舍五入-round
select round(314.15926);
select round(314.15926, 2);
select round(314.15926, -2);
向上取整-ceil
select ceil(3.1415926);
向下取整-floor
select floor(3.1415926);
绝对值
select abs( );
平方
select power(数,幂 );
开方
select sqrt( );
对数运算
select log2/ log10( );
条件函数
if (boolean testCondition, T valueTrue, T valueFalseOrNull)
//将emp表的员工工资等级分类:0-1500、1500-3000、3000以上
select sal, if (sal<=1500, 1, if (sal <= 3000, 2, 3)) from emp;
CASE WHEN a THEN b [WHEN c THEN d]* [ELSE e] END
//复杂条件用 case when 更直观
select sal, case when sal<=1500 then 1
when sal<=3000 then 2
else 3 end sallevel
from emp;
//以下语句等价
//替换员工部门编号
select ename, deptno,
case deptno when 10 then 'accounting'
when 20 then 'research'
when 30 then 'sales'
else 'unknown' end deptname
from emp;
select ename, deptno,
case when deptno=10 then 'accounting'
when deptno=20 then 'research'
when deptno=30 then 'sales'
else 'unknown' end deptname
from emp;
COALESCE(T v1, T v2, ...)
返回参数中的第一个非空值;如果所有值都为NULL,那么返回NULL
select sal, coalesce(comm, 0) from emp;
//当comm为null值的时候,将返回1,否则将返回comm的真实值
select coalesce(success_cnt,period,1) from tableA;
//当success_cnt不为null,那么无论period是否为null,都将返回success_cnt的真实值(因为success_cnt是第一个参数);
//当success_cnt为null,而period不为null的时候,返回period的真实值;
//只有当success_cnt和period均为null的时候,将返回1;
isnull(a) isnotnull(a)
select * from emp where isnull(comm);
select * from emp where isnotnull(comm);
nvl(T value, T default_value)
select empno, ename, job, mgr, hiredate, deptno, sal + nvl(comm,0) sumsal
from emp;
nullif(x, y) 相等为空,否则为第一个参数
SELECT nullif("b", "b"), nullif("b", "a");
UDTF函数
UDTF : User Defined Table-Generating Functions。用户定义表生成函数,一行输入,多行输出
explode,炸裂函数
将一行中复杂的 array 或者 map 结构拆分成多行
select explode(array('A','B','C')) as col;
select explode(map('a', 8, 'b', 88, 'c', 888));
//UDTF's are not supported outside the SELECT clause, nornested in expressions
//当使用UDTF函数的时候,hive只允许对拆分字段进行访问
SELECT pageid, explode(adid_list) AS myCol... is not supported
SELECT explode(explode(adid_list)) AS myCol... is not supported
为了解决上述问题,可以使用lateral view(侧视图)
lateral view 常与表生成函数explode结合使用
语法
lateralView:
LATERAL VIEW udtf(expression) tableAlias AS
columnAlias (',' columnAlias)*
fromClause:
FROM baseTable (lateralView)*
ateral view 的基本使用
with t1 as (
select 'OK' cola, split('www.baidu.com', '\\.') colb
)
select cola, colc
from t1
lateral view explode(colb) t2 as colc;
窗口函数
窗口函数又名开窗函数,属于分析函数的一种;用于解决复杂报表统计需求的功能强大的函数,很多场景都需要用到;
窗口函数用于计算基于组的某种聚合值,它和聚合函数的不同之处是:对于每个组返回多行,而聚合函数对于每个组只返回一行;
窗口函数指定了分析函数工作的数据窗口大小,这个数据窗口大小可能会随着行的变化而变化;
over 关键字
使用窗口函数之前一般要通过over()进行开窗
//Hive基本内建聚合函数通常与GROUP BY连用,默认情况下是对整个表进行操作。
//在使用GROUP BY时,除聚合函数外其他已选择列必须包含在GROUP BY子句中。
select ename, sal, sum(sal) salsum from emp
group by ename,sal;
//不使用窗口函数,有语法错误
select ename, sal, sum(sal) salsum from emp;
//使用窗口函数
select ename, sal, sum(sal) over( ) salsum from emp;
使用窗口函数,查询员工姓名、薪水、薪水总和薪水占总数的比例
select ename, sal, sum(sal) over() salsum,
concat(round(sal / sum(sal) over()*100, 1) || '%') ratiosal
from emp;
窗口函数是针对每一行数据的;如果over中没有参数,默认的是全部结果集;
partition by子句
在over窗口中进行分区,对某一列进行分区统计,窗口的大小就是分区的大小
select ename, sal,
sum(sal) over(partition by deptno) salsum
from emp;
order by 子句
order by 子句对输入的数据进行排序;
//增加了order by子句;sum:从分组的第一行到当前行求和
select ename, sal, deptno, sum(sal) over(
partition by deptno order by sal) salsum
from emp;
Window子句
rows between ... and ...
对窗口的结果做更细粒度的划分,五个选项
1)组内第一行数据 unbounded preceding ;
2)组内当前行的前n行数据 n preceding ;
3)当前行数据 current row ;
4)组内当前行的后n行数据 n following ;
5)组内最后一行数据 unbounded following;
图示关系
- 组内,第一行到当前行的和
select ename, sal, deptno,
sum(sal) over(partition by deptno order by ename
rows between unbounded preceding and
current row
)
from emp;
//等价
select ename, sal, deptno,
sum(sal) over(partition by deptno order by ename)
from emp;
- 组内,第一行到最后一行的和
select ename, sal, deptno,
sum(sal) over(partition by deptno order by ename
rows between unbounded preceding and
unbounded following
)
from emp;
- 组内,前一行、当前行、后一行的和
select ename, sal, deptno,
sum(sal) over(partition by deptno order by ename
rows between 1 preceding and 1 following
)
from emp;
排名函数
都是从1开始,生成数据项在分组中的排名
三种
row_number()
排名顺序增加不会重复;如1、2、3、4、... ...
RANK()
排名相等会在名次中留下空位;如1、2、2、4、5、... ...
DENSE_RANK()
排名相等会在名次中不会留下空位 ;如1、2、2、3、4、... ...
理解
row_number / rank / dense_rank
100 1 1 1
100 2 1 1
100 3 1 1
99 4 4 2
98 5 5 3
98 6 5 3
97 7 7 4
//按照班级,使用3种方式对成绩进行排名
select cname, sname, score,
row_number() over (partition by cname order by score desc) rank1,
rank() over (partition by cname order by score desc)rank2,
dense_rank() over (partition by cname order by score desc) rank3
from t2;
//求每个班级前3名的学员--前3名的定义是什么--假设使用dense_rank
select cname, sname, score, rank
from (select cname, sname, score,
dense_rank() over (partition by cname order by
score desc) rank
from t2) tmp
where rank <= 3;
序列/ 行函数
五个
1)lag——返回当前数据行的上一行数据;
2)lead——返回当前数据行的下一行数据;
3)first_value——取分组内排序后,截止到当前行,第一个值;
4)last_value——分组内排序后,截止到当前行,最后一个值;
5)ntile——将分组的数据按照顺序切分成n片,返回当前切片值;
//lag-返回当前数据行的上一行数据
//lead-功能上与lag类似
select cid, ctime, pv,
lag(pv) over(partition by cid order by ctime) lagpv,
lead(pv) over(partition by cid order by ctime) leadpv
from userpv;
//可以跟着参数,移动多行
select cid, ctime, pv,
lag(pv,2) over(partition by cid order by ctime) lagpv,
lead(pv,3) over(partition by cid order by ctime) leadpv
from userpv;
//first_value / last_value
select cid, ctime, pv,
first_value(pv) over (partition by cid order by ctime
rows between unbounded preceding and unbounded following) as firstpv,
last_value(pv) over (partition by cid order by ctime
rows between unbounded preceding and unbounded following) as lastpv
from userpv;
//ntile-按照cid进行分组,每组数据分成2份
select cid, ctime, pv,
ntile(2) over(partition by cid order by ctime) ntile
from userpv;
自定义函数
当 Hive 提供的内置函数无法满足实际的业务处理需要时,可以考虑使用用户自定义函数进行扩展;
分类
1)UDF(User Defined Function)
用户自定义函数,一进一出
2)UDAF(User Defined Aggregation Function)
用户自定义聚集函数,多进一出;类似于count/max/min
3)UDTF(User Defined Table-Generating Functions)
用户自定义表生成函数,一进多出;类似于:explode
-
UDF开发
继承org.apache.hadoop.hive.ql.exec.UDF;
需要实现evaluate函数;evaluate函数支持重载;
UDF必须要有返回类型,可以返回null,但是返回类型不能为void; -
UDF开发步骤
1)创建maven java 工程,添加依赖;
2)开发java类继承UDF,实现evaluate 方法;
3)将项目打包上传服务器;
4)添加开发的jar包;
5)设置函数与自定义函数关联;
6)使用自定义函数;
具体实现
需求:扩展系统 nvl 函数功能
创建maven java 工程,添加依赖
<dependencies>
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-exec</artifactId>
<version>2.3.7</version>
</dependency>
</dependencies>
开发java类继承UDF,实现evaluate 方法
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text;
public class nvl extends UDF {
public Text evaluate(final Text t, final Text x) {
if (t == null || t.toString().trim().length()==0) {
return x;
}
return t;
}
}
将项目打成jar包上传到服务器上;
添加开发的jar包(在Hive命令行中);
add jar /home/1 hadoop/hiveudf.jar;
创建临时函数。指定类名一定要完整的路径,即包名加类名;
create temporary function mynvl as "com.xxx.hive.udf.nvl";
使用自定义函数
//基本功能还有
select mynvl(comm, 0) from mydb.emp;
//测试扩充的功能
select mynvl("", "OK");
select mynvl(" ", "OK");
注意:
退出Hive命令行,再进入Hive命令行。执行步骤6的测试,发现函数失效,因为我们创建的函数是临时函数,我们可以通过创建永久函数解决该问题。
将jar上传HDFS
hdfs dfs -1 put hiveudf.jar jar/
Hive命令行中创建永久函数
create function mynvl1 as 'com.xxx.hive.udf.nvl' using jar
'hdfs:/user/hadoop/jar/hiveudf.jar';
退出hive再次测试
如果需要删除自定义函数,通过drop命令
drop function mynvl1;
show functions;
参考:
1.SQL——coalesce函数详解