3.2.2 数据仓库工具 -- Hive(DQL命令group by ,表连接,排序、函数、DML命令事物、元数据管理存储)

3.2.2 数据仓库工具 – Hive



六、HQL操作之–DQL命令【重点】

DQL – Data Query Language 数据查询语言
select语法:
在这里插入图片描述
在这里插入图片描述
创建表,加载数据

-- 测试数据 /home/hadoop/data/emp.dat
7369,SMITH,CLERK,7902,2010-12-17,800,,20
7499,ALLEN,SALESMAN,7698,2011-02-20,1600,300,30
7521,WARD,SALESMAN,7698,2011-02-22,1250,500,30
7566,JONES,MANAGER,7839,2011-04-02,2975,,20
7654,MARTIN,SALESMAN,7698,2011-09-28,1250,1400,30
7698,BLAKE,MANAGER,7839,2011-05-01,2850,,30
7782,CLARK,MANAGER,7839,2011-06-09,2450,,10
7788,SCOTT,ANALYST,7566,2017-07-13,3000,,20
7839,KING,PRESIDENT,,2011-11-07,5000,,10
7844,TURNER,SALESMAN,7698,2011-09-08,1500,0,30
7876,ADAMS,CLERK,7788,2017-07-13,1100,,20
7900,JAMES,CLERK,7698,2011-12-03,950,,30
7902,FORD,ANALYST,7566,2011-12-03,3000,,20
7934,MILLER,CLERK,7782,2012-01-23,1300,,10
 
-- 建表并加载数据
CREATE TABLE emp (
empno int,
ename string, 
job string, 
mgr int, 
hiredate DATE, 
sal int, 
comm int, 
deptno int
)row format delimited fields terminated by ",";
 
-- 加载数据
LOAD DATA LOCAL INPATH '/home/hadoop/data/emp.dat'
INTO TABLE emp;

第 1 节 基本查询

-- 省略from子句的查询
select 8*888 ;
select current_date ;
 
-- 使用列别名
select 8*888 product;
select current_date as currdate;
 
-- 全表查询
select * from emp;
 
-- 选择特定列查询
select ename, sal, comm from emp;
 
-- 使用函数
select count(*) 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;

第 2 节 where子句

WHERE子句紧随FROM子句,使用WHERE子句,过滤不满足条件的数据;
where 子句中不能使用列的别名;

select * from emp
where sal > 2000;

where子句中会涉及到较多的比较运算 和 逻辑运算;
比较运算符
官方文档:https://cwiki.apache.org/confluence/display/Hive/LanguageManual+UDF
在这里插入图片描述
逻辑运算符
就是我们所熟悉的:and、or、not

-- 比较运算符,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).*';

第 3 节 group by子句

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的部门
select deptno, avg(sal)
from emp
group by deptno
having avg(sal) > 2000;

第 4 节 表连接

在这里插入图片描述
在这里插入图片描述
案例演示:

-- 准备数据
u1.txt数据:
1,a
2,b
3,c
4,d
5,e
6,f
 
u2.txt数据:
4,d
5,e
6,f
7,g
8,h
9,i
 
create table if not exists u1(
id int,
name string)
row format delimited fields terminated by ',';
 
create table if not exists u2(
id int,
name string)
row format delimited fields terminated by ',';
 
load data local inpath '/home/hadoop/data/u1.txt' into table u1;
 
load data local inpath '/home/hadoop/data/u2.txt' into table u2;

多表连接
连接 n张表,至少需要 n-1 个连接条件。例如:连接四张表,至少需要三个连接条件。
多表连接查询,查询老师对应的课程,以及对应的分数,对应的学生:

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 进行连接操作;
然后再继续直到全部操作;
在这里插入图片描述

set hive.strict.checks.cartesian.product=false; 
select * from u1, u2;

第 5 节 排序子句【重点】

全局排序(order by)

order by 子句出现在select语句的结尾;
order by子句对最终的结果进行排序;
默认使用升序(ASC);可以使用DESC,跟在字段名之后表示降序;
ORDER BY执行全局排序,只有一个reduce;

扫描二维码关注公众号,回复: 12636425 查看本文章
-- 普通排序
select * from emp order by deptno;
 
-- 按别名排序, 下面的nvl是个函数, 将null转换为0
select empno, ename, job, mgr, sal + nvl(comm, 0) salcomm, deptno
from emp
order by salcomm desc;
 
-- 多列排序
select empno, ename, job, mgr, sal + nvl(comm, 0) salcomm, deptno
from emp
order by deptno, salcomm desc;
 
-- 排序字段要出现在select子句中。以下语句无法执行(因为select子句中缺少deptno):
select empno, ename, job, mgr, sal + nvl(comm, 0) salcomm
from emp
order by deptno, salcomm desc;

每个MR内部排序(sort by)

在这里插入图片描述

-- 设置reduce个数
set mapreduce.job.reduces=2;
 
-- 按照工资降序查看员工信息
select * from emp sort by sal desc;
 
-- 将查询结果导入到文件中(按照工资降序)。生成两个输出文件,每个文件内部数据按工资降序排列
insert overwrite local directory '/home/hadoop/output/sortsal'
row format delimited fields terminated by ' '
select * from emp sort by sal desc;

分区排序(distribute by)

在这里插入图片描述

启动2个reducer task;先按 deptno 分区,在分区内按 sal+comm 排序
    set mapreduce.job.reduces=2;
 
将结果输出到文件,观察输出结果
    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;
 
    上例中,所有数据都被分到了统一区,看不出分区的结果
    是由于hash分区的时候,  10,20,30 % 20 都为0,  即分区条件写错
 
 将数据分到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;

在这里插入图片描述
在这里插入图片描述

七、函数

Hive内置函数:https://cwiki.apache.org/confluence/display/Hive/LanguageManual+UDF#LanguageManualUDF-Built-inFunctions

第 1 节 系统内置函数

查看系统函数

-- 查看系统自带函数
show functions;
 
-- 显示自带函数的用法
desc function upper;
desc function extended upper;

日期函数【重要】

-- 当前前日期
select current_date;
select unix_timestamp();
 
-- 建议使用current_timestamp,有没有括号都可以
select 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表中,每个人的工龄  round(数据,保留位数)
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('lagou', 'com'));
select concat_ws(" ", ename, job) from emp;
 
-- 求子串。substr
SELECT substr('www.lagou.com', 5);
SELECT substr('www.lagou.com', -5);
SELECT substr('www.lagou.com', 5, 5);
 
-- 字符串切分。split,注意 '.' 要转义
select split("www.lagou.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);
 
-- 其他数学函数包括:绝对值abs、平方power、开方sqrt、对数运算log、三角运算等

条件函数【重要】

条件函数主要有, if, case when, coalesce, isnull/isnotnull, nvl, nullif
 
-- 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 ename,sal,
case when sal<=1500 then 1
     when sal<=3000 then 2
     else 3 end sallevel
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; 
 
select ename, deptno,
case deptno when 10 then 'accounting'
            when 20 then 'research'
            when 30 then 'sales'
            else 'unknown' end deptname
from emp;
 
-- COALESCE(T v1, T v2, ...)。返回参数中的第一个非空值;如果所有值都为NULL,那么返回NULL
select sal, coalesce(comm, 0) from emp;
 
-- isnull(a) isnotnull(a)
select * from emp where isnull(comm);
select * from emp where comn is null;
 
select * from emp where isnotnull(comm);
select * from emp where comn is not null;
 
-- nvl(T value, T default_value)
select empno, ename, job, mgr, hiredate, deptno, sal + nvl(comm,0) sumsal
from emp;
 
-- nullif(x, y) 相等为空,否则返回第一个参数x
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, nor nestedin expressions
 
 SELECT pageid, explode(adid_list) AS myCol... is not supported
 SELECT explode(explode(adid_list)) AS myCol... is notsupported
 
 
 lateral view 常与 表生成函数explode结合使用
 
-- lateral view 语法:
-- lateralView: LATERAL VIEW udtf(expression) tableAlias AS columnAlias (',' columnAlias)*
-- fromClause: FROM baseTable (lateralView)*
 
-- lateral view 的基本使用
with t1 as (
    select 'OK' cola, split('www.lagou.com', '\\.') colb
)
select cola, colc
from t1
lateral view explode(colb) t2 as colc;

UDTF 案例1:

-- 数据(uid tags):
1 1,2,3
2 2,3
3 1,2
 
--编写sql,实现如下结果:
1 1
1 2
1 3
2 2
2 3
3 1
3 2
 
-- 建表加载数据
create table tab1(
 id int,
 tags string
)
row format delimited fields terminated by '\t';
load data local inpath '/hivedata/tab1.dat' into table tab1;
 
-- SQL
select id, split(tags, ',') from tab1;
 
select uid, tag
from t1
lateral view explode(split(tags,",")) t1 as tag;

UDTF 案例2:

-- 数据准备 (score.dat)
lisi|Chinese:90,Math:80,English:70
wangwu|Chinese:88,Math:90,English:96
maliu|Chinese:99,Math:65,English:60
 
-- 创建表
create table studscore(
 name string
,score map<String,string>
)
row format delimited
fields terminated by '|'
collection items terminated by ','
map keys terminated by ':';
 
-- 加载数据
load data local inpath '/home/hadoop/data/score.dat' overwrite
into table studscore;
 
 
-- 需求:找到每个学员的最好成绩
 
-- 第一步,使用 explode 函数将map结构拆分为多行
select explode(score) as (subject, socre) from studscore;
--但是这里缺少了学员姓名,加上学员姓名后出错。下面的语句有是错的, explode函数不能和表中字段同用
select name, explode(score) as (subject, socre) from studscore;
 
-- 第二步:explode常与 lateral view 函数联用,这两个函数结合在一起能关联其他字段
select name, subject, score1 as score from studscore
lateral view explode(score) t1 as subject, score1;
 
-- 第三步:找到每个学员的最好成绩
select name, max(mark) maxscore
from (
      select name, subject, mark
      from studscore lateral view explode(score) t1 as subject, mark
     ) t1
group by name;
 
-- 做成临时表也行
with tmp as (
  select name, subject, mark
  from studscore lateral view explode(score) t1 as subject, mark
)
select name, max(mark) maxscore
from tmp
group by name;

在这里插入图片描述

第 2 节 窗口函数【重要】

在这里插入图片描述

over 关键字

使用窗口函数之前一般要要通过over()进行开窗

-- 查询emp表工资总和
select sum(sal) from emp;
 
-- 不使用窗口函数over, 是普通聚合函数, 有语法错误
select ename, sal, sum(sal) 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 ...

在这里插入图片描述

-- rows between ... and ... 子句
-- 等价。组内,第一行到当前行的和
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 currentrow
       )
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;

排名函数

在这里插入图片描述

-- 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
 
-- 数据准备
class1 s01 100
class1 s03 100
class1 s05 100
class1 s07 99
class1 s09 98
class1 s02 98
class1 s04 97
class2 s21 100
class2 s24 99
class2 s27 99
class2 s22 98
class2 s25 98
class2 s28 97
class2 s26 96
 
-- 创建表加载数据
create table t2(
    cname string,
    sname string,
    score int
) row format delimited fields terminated by '\t';
 
load data local inpath '/home/hadoop/data/t2.dat' into table t2;
 
-- 按照班级,使用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 byscore desc) rank from t2
) tmp
where rank <= 3;

序列函数

在这里插入图片描述

-- 测试数据 userpv.dat。cid ctime pv
cookie1,2019-04-10,1
cookie1,2019-04-11,5
cookie1,2019-04-12,7
cookie1,2019-04-13,3
cookie1,2019-04-14,2
cookie1,2019-04-15,4
cookie1,2019-04-16,4
cookie2,2019-04-10,2
cookie2,2019-04-11,3
cookie2,2019-04-12,5
cookie2,2019-04-13,6
cookie2,2019-04-14,3
cookie2,2019-04-15,9
cookie2,2019-04-16,7
 
-- 建表语句
create table userpv(
cid string,
ctime date,
pv int
)
row format delimited fields terminated by ",";
 
-- 加载数据
Load data local inpath '/home/hadoop/data/userpv.dat' into table userpv;
 
-- 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;
 
-- lag / lead 可以跟参数, 移动多行
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
                            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;

SQL面试题

1、连续7天登录的用户

-- 数据。uid dt status(1 正常登录,0 异常)
1 2019-07-11 1
1 2019-07-12 1
1 2019-07-13 1
1 2019-07-14 1
1 2019-07-15 1
1 2019-07-16 1
1 2019-07-17 1
1 2019-07-18 1
2 2019-07-11 1
2 2019-07-12 1
2 2019-07-13 0
2 2019-07-14 1
2 2019-07-15 1
2 2019-07-16 0
2 2019-07-17 1
2 2019-07-18 0
3 2019-07-11 1
3 2019-07-12 1
3 2019-07-13 1
3 2019-07-14 0
3 2019-07-15 1
3 2019-07-16 1
3 2019-07-17 1
3 2019-07-18 1
 
-- 建表语句
create table ulogin(
uid int,
dt date,
status int
)
row format delimited fields terminated by ' ';
 
-- 加载数据
load data local inpath '/home/hadoop/data/ulogin.dat' into table ulogin;
 
-- 连续值的求解,面试中常见的问题。这也是同一类,基本都可按照以下思路进行
-- 1、使用 row_number 在组内给数据编号(rownum)
select uid, dt,
       row_number() over (partition by uid order by dt) rownum
from ulogin
where status=1;
 
 
-- 2、某个值 - rownum = gid,得到结果可以作为后面分组计算的依据
select uid, dt,
       date_sub(dt, row_number() over (partition by uid order by dt)) gid
from ulogin
where status=1;
 
-- 3、根据求得的gid,作为分组条件,求最终结果
select uid, count(*) countlogin
from (select uid, dt,
             date_sub(dt, row_number() over (partition by uid order by dt)) gid
      from ulogin
      where status=1
) t1
group by uid, gid
having countlogin >= 7;

2、编写sql语句实现每班前三名,分数一样并列,同时求出前三名按名次排序的分差

-- 数据。sid class score
1 1901 90
2 1901 90
3 1901 83
4 1901 60
5 1902 66
6 1902 23
7 1902 99
8 1902 67
9 1902 87
 
 
-- 待求结果数据如下:
class  score  rank  lagscore
1901   90    1    0
1901   90    1    0
1901   83    2    -7
1901   60    3    -23
1902   99    1    0
1902   87    2    -12
1902   67    3    -20
 
 
-- 建表语句
create table stu(
sno int,
class string,
score int
)row format delimited fields terminated by ' ';
 
 
-- 加载数据
load data local inpath '/home/hadoop/data/stu.dat' into table stu;
 
 
-- 求解思路:
-- 1、上排名函数,分数一样并列,所以用dense_rank
select sno, class, score,
       dense_rank() over (partition by class order by score desc) as rank
from stu;
 
-- 2、将上一行数据下移,相减即得到分数差
select sno, class, score,
       dense_rank() over (partition by class order by score desc) as rank,
       score - lag(score) over (partition by class order by score desc) lagscore
from stu;
 
-- 3、处理 NULL
select class, score,
       dense_rank() over (partition by class order by score desc) as rank,
       nvl(score - lag(score) over (partition by class order by score desc), 0) lagscore
from stu;
 
with tmp as (
    select sno, class, score,
           dense_rank() over (partition by class order by score desc) as rank
    from stu
)
select class, score, rank,
       nvl(score - lag(score) over (partition by class order by score desc), 0) lagscore
from tmp
where rank<=3;

3、行 <=> 列

行转列

-- 数据。id1 id2 flag
a b 2
a b 1
a b 3
c d 6
c d 8
c d 8
 
-- 编写sql实现如下结果
id1 id2  flag
 a   b   2|1|3
 c   d   6|8
 
-- 创建表 & 加载数据
create table rowline2(
id1 string,
id2 string,
flag int
) row format delimited fields terminated by ' ';
 
load data local inpath '/root/data/data2.dat' into table rowline2;
 
-- 第一步 将元素聚拢
-- 下面的函数, 去重
select id1, id2, collect_set(flag) flag 
from rowline2 
group by id1, id2;
 
-- 下面的不去重
select id1, id2, collect_list(flag) flag 
from rowline2 
group by id1, id2;
 
-- 下面的不去重且排序
select id1, id2, sort_array(collect_set(flag)) flag 
from rowline2
group by id1, id2;
 
-- 第二步 将元素连接在一起
select id1, id2, concat_ws("|", collect_set(flag)) flag
from rowline2
group by id1, id2;
 
-- 这里报错,CONCAT_WS must be "string or array<string>"。
-- 加一个类型转换即可cast (flag as string)
select id1, id2, concat_ws("|", collect_set(cast (flag as string))) flag
from rowline2
group by id1, id2;
 
 
-- -------------------------------下面忽略
-- 创建表 rowline3
create table rowline3 
as select id1, id2, concat_ws("|", collect_set(cast (flag as string))) flag
from rowline2
group by id1, id2;
 
-- 第一步:将复杂的数据展开
select explode(split(flag, "\\|")) flat from rowline3;
 
-- 第二步:lateral view 后与其他字段关联
select id1, id2, newflag
from rowline3 lateral view explode(split(flag, "\\|")) t1 as newflag;
 
-- lateralView: LATERAL VIEW udtf(expression) tableAlias AS
-- columnAlias (',' columnAlias)*
-- fromClause: FROM baseTable (lateralView)*

列转行

-- 数据:id course
1 java
1 hadoop
1 hive
1 hbase
2 java
2 hive
2 spark
2 flink
3 java
3 hadoop
3 hive
3 kafka
 
-- 建表加载数据
create table rowline1(
id string,
course string
)row format delimited fields terminated by ' ';
 
load data local inpath '/root/data/data1.dat' into table rowline1;
 
-- 编写sql,得到结果如下(1表示选修,0表示未选修)
id   java  hadoop   hive  hbase  spark  flink  kafka
1      1      1      1      1      0      0      0
2      1      0      1      0      1      1      0
3      1      1      1      0      0      0      1
 
-- 使用case when;group by + sum
select id,
       sum(case when course="java" then 1 else 0 end) as java,
       sum(case when course="hadoop" then 1 else 0 end) as hadoop,
       sum(case when course="hive" then 1 else 0 end) as hive,
       sum(case when course="hbase" then 1 else 0 end) as hbase,
       sum(case when course="spark" then 1 else 0 end) as spark,
       sum(case when course="flink" then 1 else 0 end) as flink,
       sum(case when course="kafka" then 1 else 0 end) as kafka
from rowline1
group by id;

小结:
case when + sum + group by
collect_set、collect_list、concat_ws
sort_array
explode + lateral view

第 3 节 自定义函数

在这里插入图片描述
需求:扩展系统 nvl 函数功能:

nvl(x, y): x==null => 返回y
nvl(x, y): x==null or x=="" or x=="   " =>返回y

1、创建maven java 工程,添加依赖

    <!-- pom.xml 文件 -->
    <dependencies>
        <dependency>
            <groupId>org.apache.hive</groupId>
            <artifactId>hive-exec</artifactId>
            <version>2.3.7</version>
        </dependency>
    </dependencies>
 
    <!--maven打包插件 -->
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-assembly-plugin </artifactId>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
 
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

2、开发java类继承UDF,实现evaluate 方法

package com.lagou.hive.udf;
 
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text;
 
/**
 * @author CH
 * @date 2020/12/28 17:07
 */
public class nvl extends UDF {
    
    
 
    public Text evaluate(final Text x, final Text y) {
    
    
 
        if (null == x || 0 == x.toString().trim().length()) {
    
    
            return y;
        }
 
        return x;
    }
}

3、将项目打包上传服务器
4、添加开发的jar包(在Hive命令行中)

add jar /home/hadoop/hiveudf.jar;

5、创建临时函数。指定类名一定要完整的路径,即包名加类名

create temporary function mynvl as "com.lagou.hive.udf.nvl";

6、执行查询

-- 基本功能还有 
select mynvl(comm, 0) from mydb.emp; 
-- 测试扩充的功能 
select mynvl("", "OK"); select mynvl(" ", "OK");

7、退出Hive命令行,再进入Hive命令行。执行步骤6的测试,发现函数失效。
备注:创建临时函数每次进入Hive命令行时,都必须执行以下语句,很不方便:

add jar /home/hadoop/hiveudf.jar; 
create temporary function mynvl as "cn.lagou.hive.udf.nvl";

可创建永久函数:
1、将jar上传HDFS

hdfs dfs -mkdir -p jar/

hdfs dfs -put hiveudf.jar jar/

2、在Hive命令行中创建永久函数

create function mynvl1 as 'com.lagou.hive.udf.nvl' using jar
'hdfs:/user/root/jar/nvl.jar';
 
-- 查询所有的函数,发现 mynvl1 在列表中
show functions;

3、退出Hive,再进入,执行测试

-- 基本功能还有
select mynvl(comm, 0) from mydb.emp;
 
-- 测试扩充的功能
select mynvl("", "OK");
select mynvl(" ", "OK");

4、删除永久函数,并检查

drop function mynvl1; 
show functions;

第八部分 HQL操作之–DML命令

在这里插入图片描述

第 1 节 Hive 事务

在这里插入图片描述
在这里插入图片描述

第 2 节 Hive 事务操作示例

-- 这些参数也可以设置在hive-site.xml中
SET hive.support.concurrency = true;
-- Hive 0.x and 1.x only
SET hive.enforce.bucketing = true;
SET hive.exec.dynamic.partition.mode = nonstrict;
SET hive.txn.manager = org.apache.hadoop.hive.ql.lockmgr.DbTxnManager;
 
-- 创建表用于更新。满足条件:内部表、ORC格式、分桶、设置表属性
create table zxz_data(
    name string,
    nid int,
    phone string,
    ntime date)
clustered by(nid) into 5 buckets
stored as orc
tblproperties('transactional'='true');
 
-- 创建临时表,用于向分桶表插入数据
create table temp1(
    name string,
    nid int,
    phone string,
    ntime date)
row format delimited
fields terminated by ",";
 
-- 数据
name1,1,010-83596208,2020-01-01
name2,2,027-63277201,2020-01-02
name3,3,010-83596208,2020-01-03
name4,4,010-83596208,2020-01-04
name5,5,010-83596208,2020-01-05
 
-- 向临时表加载数据;向事务表中加载数据
load data local inpath '/home/hadoop/data/zxz_data.txt' overwrite into table temp1;
insert into table zxz_data select * from temp1;
 
-- 检查数据和文件
select * from zxz_data;
dfs -ls /user/hive/warehouse/mydb.db/zxz_data ;
 
-- DML 操作
delete from zxz_data where nid = 3;
 
dfs -ls /user/hive/warehouse/mydb.db/zxz_data ;
 
insert into zxz_data values ("name3", 3, "010-83596208", current_date);  -- 不支持
 
insert into zxz_data values ("name3", 3, "010-83596208", "2020-06-01");  -- 执行
 
insert into zxz_data select "name3", 3, "010-83596208", current_date;
 
dfs -ls /user/hive/warehouse/mydb.db/zxz_data ;
 
insert into zxz_data values
        ("name6", 6, "010-83596208", "2020-06-02"),
        ("name7", 7, "010-83596208", "2020-06-03"),
        ("name8", 9, "010-83596208", "2020-06-05"),
        ("name9", 8, "010-83596208", "2020-06-06");
 
dfs -ls /user/hive/warehouse/mydb.db/zxz_data ;
 
-- name后拼接00
update zxz_data set name=concat(name, "00") where nid>3;
 
dfs -ls /user/hive/warehouse/mydb.db/zxz_data ;
 
-- 分桶字段不能修改,下面的语句不能执行
-- Updating values of bucketing columns is not supported
update zxz_data set nid = nid + 1;

第九部分 元数据管理与存储

第 1 节 Metastore

在这里插入图片描述

metastore三种配置方式

1、内嵌模式

内嵌模式使用的是内嵌的Derby数据库来存储元数据,也不需要额外起Metastore服务。数据库和Metastore服务都嵌入在主Hive Server进程中。这个是默认的,配置简单,但是一次只能一个客户端连接,适用于用来实验,不适用于生产环境。
优点:配置简单,解压hive安装包 bin/hive 启动即可使用;
缺点:不同路径启动hive,每一个hive拥有一套自己的元数据,无法共享。
在这里插入图片描述

2、本地模式

本地模式采用外部数据库来存储元数据,目前支持的数据库有:MySQL、Postgres、Oracle、MS SQL Server。教学中实际采用的是MySQL。
本地模式不需要单独起metastore服务,用的是跟Hive在同一个进程里的metastore服务。也就是说当启动一个hive 服务时,其内部会启动一个metastore服务。Hive根据 hive.metastore.uris 参数值来判断,如果为空,则为本地模式。
缺点:每启动一次hive服务,都内置启动了一个metastore;在hive-site.xml中暴露的数据库的连接信息;
优点:配置较简单,本地模式下hive的配置中指定mysql的相关信息即可。
在这里插入图片描述

3. 远程模式

远程模式下,需要单独起metastore服务,然后每个客户端都在配置文件里配置连接到该metastore服务。远程模式的metastore服务和hive运行在不同的进程里。在生产环境中,建议用远程模式来配置Hive Metastore。
在这种模式下,其他依赖hive的软件都可以通过Metastore访问Hive。此时需要配置hive.metastore.uris 参数来指定 metastore 服务运行的机器ip和端口,并且需要单独手动启动metastore服务。metastore服务可以配置多个节点上,避免单节点故障导致整个集群的hive client不可用。同时hive client配置多个metastore地址,会自动选择可用节点。
在这里插入图片描述
metastore内嵌模式配置

1、下载软件解压缩
2、设置环境变量,并使之生效
3、初始化数据库。 schematool -dbType derby -initSchema
4、进入hive命令行
5、再打开一个hive命令行,发现无法进入

metastore远程模式配置
配置规划:
在这里插入图片描述
配置步骤:
1、将 linux123 的 hive 安装文件拷贝到 linux121、linux122

[root@linux123 servers]# scp -r hive-2.3.7/ linux121:KaTeX parse error: Expected 'EOF', got '#' at position 28: …nux123 servers]#̲ scp -r hive-2.…PWD
#环境变量配置拷贝
[root@linux123 etc]# scp profile linux121:$ PWD
[root@linux123 etc]# scp profile linux123:$PWD

2、在linux121、linux123上分别启动 metastore 服务

#启动 metastore 服务
nohup hive --service metastore &
#查询9083端口(metastore服务占用的端口)
lsof -i:9083
#安装lsof
yum install lsof -y

3、修改 linux122 上hive-site.xml。删除配置文件中:MySQL的配置、连接数据库的用户名、口令等信息;增加连接metastore的配置:

<!-- hive metastore 服务地址, 给多个值,即为设置多个客户机 -->
<property>
    <name>hive.metastore.uris</name>
    <value>thrift://linux121:9083,thrift://linux123:9083</value>
</property>

4、启动hive。此时client端无需实例化hive的metastore,启动速度会加快。

#分别在linux121、linux123上执行以下命令,查看连接情况
lsof -i:9083

5、高可用测试。关闭已连接的metastore服务,发现hive连到另一个节点的服务上,仍然能够正常使用。

第 2 节 HiveServer2

在这里插入图片描述
在这里插入图片描述
HiveServer2配置
配置规划:
在这里插入图片描述

配置步骤:

linux121上:
1、修改集群上的 core-site.xml,增加以下内容:

<!-- HiveServer2 连不上10000;hadoop为安装用户 -->
<!-- root用户可以代理所有主机上的所有用户 -->
    <property>  
        <name>hadoop.proxyuser.root.hosts</name>  
        <value>*</value>
    </property>
    <property>  
        <name>hadoop.proxyuser.root.groups</name>  
        <value>*</value>
    </property>
    <property>
        <name>hadoop.proxyuser.hadoop.hosts</name>
        <value>*</value>
    </property>
    <property>
        <name>hadoop.proxyuser.hadoop.groups</name>
        <value>*</value>
    </property>

2、修改 集群上的 hdfs-site.xml,增加以下内容:

<!-- HiveServer2 连不上10000;启用 webhdfs 服务 -->
    <property> 
        <name>dfs.webhdfs.enabled</name> 
        <value>true</value> 
    </property>

3、启动linux123上的 HiveServer2 服务

#启动 hiveserver2 服务
nohup hiveserver2 &
#检查 hiveserver2 端口
lsof -i:10000

#从2.0开始,HiveServer2提供了WebUI
#还可以使用浏览器检查hiveserver2的启动情况。http://linux123:10002/

4、启动 linux122 节点上的 beeline
Beeline是从 Hive 0.11版本引入的,是 Hive 新的命令行客户端工具。

cd /opt/lagou/servers/hive-2.3.7/bin
[root@linux122 bin]# ./beeline

Hive客户端工具后续将使用Beeline 替代 Hive 命令行工具 ,并且后续版本也会废弃掉 Hive 客户端工具。

!connect jdbc:hive2://linux123:10000
 
用户名密码不用输入 直接回车
 
use mydb;
show tables;
select * from emp;
create table tabtest1 (c1 int, c2 string);
 
还可以连接数据库, 但是这里需要输入用户名密码
!connect jdbc:mysql://linux123:3306
!help
!quit

第 3 节 HCatalog

在这里插入图片描述

# 进入 hcat 所在目录。$HIVE_HOME/hcatalog/bin 
cd $HIVE_HOME/hcatalog/bin 
# 执行命令,创建表 
./hcat -e "create table default.test1(id string, name string, age int)" 
# 长命令可写入文件,使用 -f 选项执行 
./hcat -f createtable.txt 
# 查看元数据 
./hcat -e "use mydb; show tables" 
# 查看表结构 
./hcat -e "desc mydb.emp" 
# 删除表 
./hcat -e "drop table default.test1"

第 4 节 数据存储格式

Hive支持的存储数的格式主要有:TEXTFILE(默认格式) 、SEQUENCEFILE、RCFILE、ORCFILE、PARQUET。

  • textfile为默认格式,建表时没有指定文件格式,则使用TEXTFILE,导入数据时会直接把数据文件拷贝到hdfs上不进行处理;
  • sequencefile,rcfile,orcfile格式的表不能直接从本地文件导入数据,数据要先导入到textfile格式的表中, 然后再从表中用insert导入sequencefile、rcfile、orcfile表中。

行存储与列存储

行式存储下一张表的数据都是放在一起的,但列式存储下数据被分开保存了。

行式存储:
优点:数据被保存在一起,insert和update更加容易
缺点:选择(selection)时即使只涉及某几列,所有数据也都会被读取

列式存储:
优点:查询时只有涉及到的列会被读取,效率高
缺点:选中的列要重新组装,insert/update比较麻烦

TEXTFILE、SEQUENCEFILE 的存储格式是基于行b的;
RCFILE, ORC和PARQUET 是基于列式存储的。
在这里插入图片描述

TextFile

Hive默认的数据存储格式,数据不做压缩,磁盘开销大,数据解析开销大。 可结合Gzip、Bzip2使用(系统自动检查,执行查询时自动解压),但使用这种方式,hive不会对数据进行切分,从而无法对数据进行并行操作。

create table if not exists uaction_text(
    userid string,
    itemid string,
    behaviortype int,
    geohash string,
    itemcategory string,
    time string
)
row format delimited fields terminated by ','
stored as textfile;
 
load data local inpath '/home/hadoop/data/uaction.dat'
overwrite into table uaction_text;

SEQUENCEFILE

SequenceFile是Hadoop API提供的一种二进制文件格式,其具有使用方便、可分割、可压缩的特点。 SequenceFile支持三种压缩选择:none,record,block
Record压缩率低,一般建议使用BLOCK压缩。

RCFile

RCFile全称Record Columnar File,列式记录文件,是一种类似于SequenceFile的键值对数据文件。RCFile结合列存储和行存储的优缺点,是基于行列混合存储的RCFile。
RCFile遵循的“先水平划分,再垂直划分”的设计理念。先将数据按行水平划分为行组,这样一行的数据就可以保证存储在同一个集群节点;然后在对行进行垂直划分。
在这里插入图片描述
在这里插入图片描述

ORCFile

在这里插入图片描述
在这里插入图片描述
ORC在每个文件中提供了3个级别的索引:文件级、条带级、行组级。借助ORC提供的索引信息能加快数据查找和读取效率,规避大部分不满足条件的查询条件的文件和数据块。使用ORC可以避免磁盘和网络IO的浪费,提升程序效率,提升整个集群的工作负载。
事务表必须使用ORC格式.

create table if not exists uaction_orc(
    userid string,
    itemid string,
    behaviortype int,
    geohash string,
    itemcategory string,
    time string
)
stored as orc;
 
insert overwrite table uaction_orc select * from uaction_text;

Parquet

Apache Parquet是Hadoop生态圈中一种新型列式存储格式,它可以兼容Hadoop生态圈中大多数计算框架(Mapreduce、Spark等),被多种查询引擎支持(Hive、Impala、Drill等),与语言和平台无关的

Parquet文件是以二进制方式存储的,不能直接读取的,文件中包括实际数据和元数据,Parquet格式文件是自解析的。
在这里插入图片描述
Row group:

  • 写入数据时的最大缓存单元
  • MR任务的最小并发单元
  • 一般大小在50MB-1GB之间

Column chunk:

  • 存储当前Row group内的某一列数据
  • 最小的IO并发单元

Page:

  • 压缩、读数据的最小单元
  • 获得单条数据时最小的读取数据单元
  • 大小一般在8KB-1MB之间,越大压缩效率越高

Footer:

  • 数据Schema信息
  • 每个Row group的元信息:偏移量、大小
  • 每个Column chunk的元信息:每个列的编码格式、首页偏移量、首索引页偏移量、个数、大小等信息
create table if not exists uaction_parquet(
userid string,
itemid string,
behaviortype int,
geohash string,
itemcategory string,
time string)
stored as parquet;
 
insert overwrite table uaction_parquet select * from uaction_text;

文件存储格式对比测试

说明:
1、给 linux123 分配合适的资源。2core;2048G内存
2、适当减小文件的数据量(现有数据约800W,根据自己的实际选择处理100-300W条数据均可)

# 检查文件行数 
wc -l uaction.dat 
#
head -n 1000000 uaction.dat > uaction1.dat 
tail -n 1000000 uaction.dat > uaction2.dat

文件压缩比

hive (mydb)> dfs -ls /user/hive/warehouse/mydb.db/ua*;
#结果
13517070 /user/hive/warehouse/mydb.db/uaction_orc/000000_1000 34867539 /user/hive/warehouse/mydb.db/uaction_parquet/000000_1000 90019734 /user/hive/warehouse/mydb.db/uaction_text/useraction.dat

ORC > Parquet > text
执行查询

SELECT COUNT(*) FROM uaction_text; 
SELECT COUNT(*) FROM uaction_orc; 
SELECT COUNT(*) FROM uaction_parquet; 

-- text : 14.446 
-- orc: 0.15 
-- parquet : 0.146

在生产环境中,Hive表的数据格式使用最多的有三种:TextFile、ORCFile、Parquet。

  • TextFile文件更多的是作为跳板来使用(即方便将数据转为其他格式)
  • 有update、delete和事务性操作的需求,通常选择ORCFile
  • 没有事务性要求,希望支持Impala、Spark,建议选择Parquet

猜你喜欢

转载自blog.csdn.net/weixin_47134119/article/details/113521742