🐎本文来源:整理自黑马程序员B站课程
🔗课程链接:www.bilibili.com/video/BV1L5…
✒️本文作者:Anthony_4926
1. DML - Load 加载数据
不管路径在哪里,只有把数据文件移动到对应的表文件夹下面,Hivve 才能映射解析成功;
最原始暴力的方式就是使用 hadoop fs-put|-mv 等方式直接将数据移动到表文件夹下
但是,Hive 官方推荐使用 Load 命令将数据加载到表中。
1.1. 功能
Load 英文单词的含义为:加载、装载;
所谓加载是指:将数据文件移动到与 Hive 表对应的位置,移动时是纯复制、移动操作。
纯复制、移动指在数据 1oad 加载到表中时,Hive 不会对表中的的数据内容进行任何转换,任何操作。
1.2. 语法规则
LOAD DATA [LOCAL] INPATH 'filepath' [OVERWRITE] INTO TABLE tablename
[PARTITION (partcol1=val1,...)]
-- 3.o 后还支持指定serde
[INPUTFORMAT 'inputformat' SERDE 'serde']
- Filepath
- filepath 表示待移动数据的路径。可以指向文件(在这种情况下,Hlive 将文件移动到表中),也可以指向目录(在这种情况下,Hive 将把该目录中的所有文件移动到表中)。
- filepath 文件路径支持下面三种形式,要结合 LOCAL 关键字一起考虑:
- 相对路径,例如:project/datal
- 绝对路径,例如:/user/hive/project/datal
- 具有 schema 的完整 URI,例如:hdfs://namenode:9000/user/hive/project/datal
- Local
- 指定 LOCAL,将在本地文件系统中查找文件路径。
- 若指定相对路径,将相对于用户的当前工作目录进行解释;
- 用户也可以为本地文件指定完整的 URI-例如:file:///user/hiive/project/datal
- 从本地加载就是复制操作
- 没有指定 LOCAL 关键字。
- 如果 filepath 指向的是一个完整的 URI,会直接使用这个 URI;
- 如果没有指定 schema,Hive 会使用在 hadoop 配置文件中参数 fs.default.name 指定的(不出意外,都是 HDFS)。
- 不是本地加载就是移动操作
- 指定 LOCAL,将在本地文件系统中查找文件路径。
- Overwrite
如果使用了 OVERWRITE 关键字,则目标表(或者分区)中的已经存在的数据会被删除,然后再将 filepath 指向的文件/目录中的内容添加到表/分区中。
Local 本地是哪里?
如果对 HiveServer2 服务运行此命令
本地文件系统指的是 Hiveserver2 服务所在机器的本地 Linux 文件系统,不是 Hive 客户端所在的本地文件系统。
1.3. Hive 3.0 新特性
Hive3.0+,load 加载数据时除了移动、复制操作之外,在某些场合下还会将加载重写为 INSERT AS SELECT。
Hive3.0+,还支持使用 inputformat、SerDe 指定输入格式,例如如 Text,ORC 等。
比如,如果表具有分区,则 load 命令没有指定分区,则将 1oad 车传换为 INSERT AS SELECT,并假定最后一组列为分区列,如果文件不符合预期,则报错。
例子如下
本来加载的时候没有指定分区,语句是报错的,但是文件的格式符合表的结构,前两个是 col1,col2,最后一个是分区字
段 col3,则此时会将 load 语句转换成为 insert as select 语狗。
--hive 3.0load命令新特性
CREATE TABLE if not exists tab1 (col1 int, col2 int)
PARTITIONED BY (col3 int)
row format delimited fields terminated by ',';
LOAD DATA LOCAL INPATH '/root/hivedata/tab1.txt' IINTO TABLE tab1;
--tab1.txt内容如下
11,22,1
33,44,2
2. Hive insert 使用方式
2.1. 背景:RDBMS 中如何使用 insert
在 MySQL 这样的 RDBMS 中,通常使用 insert+values 的方式来向表表插入数据,并且执行速度很快。
这也是 RDBMS 中表插入数据的核心方式。
INSERT INTO table_name (field1, field2,...fieldN)
VALUES
(value1, value2,...valueN);
假如把 Hive 当成 RDBMS,用 insert+values 的方式插入数据,会如何?
执行过程非常非常慢,原因在于底层是使用 MapReduce 把数据写入 Hive 表中
试想一下,如果在 Hive 中使用 insert+values,对于大数据环境一条条插入数据,用时难以想象。
Hive 官方推荐加载数据的方式:
清洗数据成为结构化文件,再使用 Load 语法加载数据到表中。这样的效率更高。
但是并不意味 insert 语法在 Hive 中没有用武之地。
2.2. insert+select
insert+select 表示:将后面查询返回的结果作为内容插入到指定表中,注意 OVERWRITE 将覆盖已有数据。
需要保证查询结果列的数目和需要插入数据表格的列数目一致。
如果查询出来的数据类型和插入表格对应的列数据类型不一致,将会进行转换,但是不能保证转换一定成功,转换
失败的数据将会为 NULL。
INSERT OVERWRITE TABLE tablename1 [PARTITION (partcol1=val1,
partcol2=val2 ...) [IF NOT EXISTS]] select_statement1 FROM from_statement;
INSERT INTO TABLE tablename1 [PARTITION (partcol1=val1, partcol2=val2 ...)]
select_statement1 FROM from_statement;
2.3. Multiple inserts 多重插入
翻译为多次插入,多重插入,其核心功能是:一次扫描,多次插入。
语法目的就是减少扫描的次数,在一次扫描中。完成多次 insert 操作。
假设我们现在有一张 student 表,现在新建了两张表,student_insert1、student_insert2,想从 student 查询数据插入到这两张表中。
--当前库下已有一张表student
select * from student;
--创建两张新表
create table student_insert1(sno int);
create table student_insert2(sname string);
-- 正常思路来讲
insert into student_insert1
select num from student;
insert into student_insert2
select name from student;
--多重插入
from student
insert overwrite table student_insert1
select num
insert overwrite table student_insert2
select name;
2.4. Insert Directory 导出数据
Hive 支持将 select 查询的结果导出成文件存放在文件系统中。语法格式如下;
注意:导出操作是一个 OVERWRITE 覆盖操作,慎重。
--标准语法:
INSERT OVERWRITE [LOCAL] DIRECTORY directory1
[ROW FORMAT row_format] [STORED AS file_format]
SELECT ... FROM ...
--Hive extension (multiple inserts):
FROM from statement
INSERT OVERWRITE [LOCAL] DIRECTORY directory1 select_sstatement
[INSERT OVERWRITE [LOCAL] DIRECTORY directory2 select[statement2] ...
--row format
: DELIMITED [FIELDS TERMINATED BY char [ESCAPED BYY char]][COLLECTION ITEMS
TERMINATED BY char]
[MAP KEYS TERMINATED BY char] [LINES TERMINATED BYchar
- 目录可以是完整的 URI。如果未指定 scheme,则 Hive 将使用 hadoop 配置变量 fs.default.name 来决定导出位置;
- 如果使用 LOCAL 关键字,则 Hive 会将数据写入本地文件系统上的目录;
- 写入文件系统的数据被序列化为文本,列之间用\001 隔开,行之间用换行符隔开。如果列都不是原始数据类型,那么这些列将序列化为 JSON 格式。也可以在导出的时候指定分隔符符换行符和文件格式。
3. 再说 Hive 事务表
3.1. 前置回顾
3.2. Hive 事务表实现原理
Hive 的文件是存储在 HDFS 上的,而 HDFS 上又不支持对文件的任意修改,只能是采取另外的手段来完成。
- 用 HDFS 文件作为原始数据(基础数据),用 delta 保存事务操作的记录增量数据;
- 正在执行中的事务,是以一个 staging 开头的文件夹维护的,执行结束就是 delta 文件夹。每次执行一次事务操作都会有这样的一个 delta 增量文件夹;
- 当访问 Hive 数据时,根据 HDFS 原始文件和 delta 增量文件做合并,查询最新的数据。
- INSERT 语句会直接创建 delta 目录;
- DELETE 目录的前缀是 delete_delta;
- UPDATE 语句采用了 split-update 特性,即先删除、后插入;
3.3. 实现原理之 delta 文件夹命名格式
- delta_minWID_maxWID_stmtID,即 delta 前缀、写事务的 ID 范围、以及语句 ID;删除时前缀是 delete_delta,里面包含了要删除的文件;
- Hive 会为写事务(INSERT、DELETE 等)创建一个写事务 ID(WriteID),该 ID 在表范围内唯一;
- 语句 ID(StatementID)则是当一个事务中有多条写入语句日时使用的,用作唯一标识。
每个事务的 delta 文件夹下,都有两个文件:
- _orc_acid_version 的内容是 2,即当前 ACID 版本号是 2。和版本 1 的主要区别是 UPDATE 语句采用了 split-update 特性,即先删除、后插入。这个文件不是 ORC 文件,可以下载下来直接接查看
bucket_0000 文件则是写入的数据内容。如果事务表没有分区和分桶,就只有一个这样的文件。文件都以 ORC 格式存储,底层二级制,需要使用 ORC TOOLS 查看,详见附件资料。
- operation:0 表示插入,1 表示更新,2 表示删除。由于使用了 split-update,UPDATE 是不会出现的,所以
- delta 文件中的 operation 是 0,delete_delta 文件中的 operation 是 2。
- originalTransaction、currentTransaction:该条记录的原始写写事务 ID,当前的写事务 ID。
- rowId:一个自增的唯一 ID,在写事务和分桶的组合中唯一。
- row:具体数据。对于 DELETE 语句,则为 null,对于 INSERT 就是插入的数据,对于 UPDATE 就是更新后的数据。
3.4. 合并器
- 随着表的修改操作,创建了越来越多的 delta 增量文件,就需要合并以保持足够的性能。
- 合并器 Compactor 是一套在 Hive Metastore 内运行,支持 ACID 系统的后台进程。所有合并都是在后台完成的,不会阻止数据的并发读、写。合并后,系统将等待所有旧文件的读操作非完成后,删除旧文件。
- 合并操作分为两种,minor compaction(小合并)、major compaaction(大合并):
- 小合并会将一组 delta 增量文件重写为单个增量文件,默认触发条件为 10 个 delta 文件;
- 大合并将一个或多个增量文件和基础文件重写为新的基础文件,默认触发条件为 delta 文件相应于基础文件占比,10%。
3.5. 局限性
虽然 Hive 支持了具有 ACID 语义的事务,但是在使用起来,并没有像像在 MySQL 中使用那样方便,有很多限制;
- 尚不支持 BEGIN,COMMIT 和 ROLLBACK,所有语言操作都是自动提交的;
- 表文件存储格式仅支持 ORC(STOREDAS ORC);
- 需要配置参数开启事务使用;
- 外部表无法创建为事务表,因为 Hive 只能控制元数据,无法管理数据
- 表属性参数 transactional 必须设置为 true;
- 必须将 Hive 事务管理器设置为 org.apache.hadoop.hive.ql.loockmgr.DbTxnManager 才能使用 ACID 表;
- 事务表不支持 LOAD DATA...语句。
3.6. 设置参数
- 客户端参数
-- 可以使用set设置当前session生效也可以配置在hive-site.xml中)
set hive.support.concurrency=true;---Hive是否支持并发
set hive.enforce.bucketing=true;--从Hive2.0开始不再需要是否开启分桶功能
set hive.exec.dynamic.partition.mode=nonstrict;--动态分区模式 非严格
set hive.txn.manager = org.apache.hadoop.hive.ql.lockmg.DbTxnManager;
- 服务端参数
set hive.compactor.initiator.on=true;--是否在Metastore实例上运行启动压缩合并
set hive.compactor.worker.threads=1;--在此metastore实例上运行多少个合并程序工作线程。
4. Select 查询数据
从哪里查询取决于 FROM 关键字后面的 table_reference。可以是普通物理表、视图、join 结果或子查询结果。
表名和列名不区分大小写。
[WITH CommonTableExpression (, CommonTableExpressionh)*]
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];
4.1. 基础语法
- where
在 WHERE 表达式中,可以使用 Hive 支持的任何函数和运算符,但聚合函数除外。那么为什么不能在 where 子句中使用聚合函数呢? 因为聚合函数要使用它的前提是结果集已经确定。而 where 子句还处于"确定"结果集的过程中,因而不能使用聚合函数。
从 Hive0.13 开始,WHERE 子句支持某些类型的子查询。
- Group by
GROUPBY 语句用于结合聚合函数,根据一个或多个列对结果集进行分组。
注意:出现在 GROUPBY 中 select_expr 的字段:要么是 GROUPBY 分组的字段;要么是被聚合函数应用的字段。 原因:避免出现一个字段多个值的歧义。
分组字段出现 select_expr 中,一定没有歧义,因为就是基于详该字段分组的,同一组中必相同;
被聚合函数应用的字段,也没歧义,因为聚合函数的本质就是多进一出,最终返回一个结果。
- Having
在 SQL 中增加 HAVING 子句原因是,WHERE 关键字无法与聚合函数数一起使用。
HAVING 子句可以让我们筛选分组后的各组数据,并且可以在 Having 中使用聚合函数,因为此时 where,group by 已经执行结束,结果集已经确定。
4.2. 执行顺序
在查询过程中执行顺序:from > where > group (含聚合) > having > order > select
- 聚合语句(sum,min,max,avg,count)要比 having 子句优先执行
- where 子句在查询过程中执行优先级别优先于聚合语句(sum,miln, max, avg, count)
结合下面 SQL 感受:
select state, sum(deaths) as cnts
from t_usa_covid19_p
where count_date = '2021-01-28'
group by state
having cnts > 10000;
4.3. 高阶语法
- Order by
Hive SQL 中的 ORDER BY 语法类似于标准 SQL 语言中的 ORDERBY 语法,会对输出的结果进行全局排序。
因此当底层使用 MapReduce 引擎执行的时候,只会有一个 reducetask 执行。如果输出的行数太大,会导致需要很长的时间才能能完成全局排序。
默认排序为升序(ASC),也可以指定为 DESC 降序。
在 Hive2.1.0 和更高版本中,支持在 ORDERBY 子句中为每个列宿定 null 类型结果排序顺序。
ASC 顺序的默认空排序顺序为 NULLS FIRST,而 DESC 顺序的默认空排序顺序为 NULLS LAST。
- Cluster by
根据指定字段将数据分组,每组内再根据该字段正序排序(只能正序)。
概况起来就是:根据同一个字段,分且排序。
分组规则 hash 散列(分桶表规则一样):Hash_Func(col_name) % reducetask个数
分为几组取决于 reducetask 的个数,默认是 1,即不排序
set mapreduce.job.reduces = 2;
select * from student cluster by num;
执行结果:分为两个部分,部分内部正序排序
- Distribute by + Sort by
需求:根据 sex 性别分为两个部分,每个分组内再根据 age 年冷的倒序排序。
CLUSTER BY 无法单独完成,因为分和排序的字段只能是同一个;
ORDER BY 更不能在这里使用,因为是全局排序,只有一个输出,无法满足分的需求。
select * from student cluster by sex order byage desc -- 不可行
select * from student cluster by sex sort by age desc -- 不可行
DISTRIBUTE BY + SORT BY 就相当于把 CLUSTER BY 的功能一分为二:
DISTRIBUTE BY 负责根据指定字段分组;
SORT BY 负责分组内排序规则。
分组和排序的字段可以不同。
select * from student distribute by sex sort by age desc;
如果 DISTRIBUTEBY+SORTBY 的字段一样,则:CLUSTER BY=DISTRIBUTE BY +SORT BY
4.4. 排序对比
- order by 全局排序,因此只有一个 reducer,结果输出在一个个文件中,当输入规模大时,需要较长的计算时间。
- distribute by 根据指定字段将数据分组,算法是 hash 散列列。sort by 是在分组之后,每个组内局部排序。
- cluster by 既有分组,又有排序,但是两个字段只能是同一个字段。
4.5. Union 联合查询
UNION 用于将来自于多个 SELECT 语句的结果合并为一个结果集。
- 使用 DISTINCT 关键字与只使用 UNION 默认值效果一样,都会删除重复行。1.2.0 之前的 Hive 版本仅支持 UNION ALL,在这种情况下不会消除重复的行。
- 使用 ALL 关键字,不会删除重复行,结果集包括所有 SELECT 语句的四匹配行(包括重复行)。
- 每个 select_statement 返回的列的数量和名称必须相同。
select_statement
union [ALL|DISTINCT]
select_statement
union [ALL|DISTINCT]
select_statement...;
如果要将 ORDER BY,SORT BY,CLUSTER BY,DISTRIBUTE BY 或 LIMIT 应用于单个 SELECT,请将子句放在括住 SELECT 的括号内,否则会报错
SELECT num,name FROM (select num, hame from student_local LIMIT 2) subq1
UNION
SELECT num,name FROM (select num,name from studdent_hdfs LIMIT 3) subq2
如果要将 ORDER BY,SORT BY,CLUSTER BY,DISTRRIBUTE BY 或 LIMIT 子句应用于整个 UNION 结果,请将 ORDER BY,SORT BY,CLUSTER BY,DISTRIBUTEBY 或 LIMIT 放在最后一个之后。
select num,name from student_local
UNION
select num,name from student_hdfs
order by num desc;
4.6. from 子句中子查询
在 Hive0.12 版本,仅在 FROM 子句中支持子查询。
必须要给子查询一个名称,因为 FROM 子句中的每个表都必须有一个名称。子查询返回结果中的列必须具有唯一的名称。子查询返回结果中的列在外部查询中可用,就像真实表的列一样。子查询也可以是带有 UNION 的查询表达式。
Hive 支持任意级别的子查询,也就是所谓的嵌套子查询。
Hive0.13.0 和更高版本中的子查询名称之前可以包含可选关键字 AS。
--from子句中子查询(Subqueries)
--子查询
SELECT num
FROM (
select num,name from student_local
) tmp;
--包含UNION ALL的子查询的示例
SELECT t3.name
FROM (
select num,name from student_local
UNION distinct
select num,name from student_hdfs
) t3;
4.7. where 子句中子查询
从 Hivel:0.13 开始,WHERE 子句支持下述类型的子查询
- 不相关子查询:该子查询不引用父查询中的列,可以将查询结果视为 IN 和 NOTIN 语句的常量;
- 相关子查询:子查询引用父查询中的列;
--where子句中子查询(Subqueries)
--不相关子查询,相当于IN、NOTIN,子查询只能选择一个列。
--(1)执行子查询,其结果不被显示,而是传递给外部查询,作为外部查询
的条件使用。
(2)执行外部查询,并显示整个结果。
SELECT
FROM student_hdfs
WHERE student_hdfs.num IN (select num from student_local limit 2);
--相关子查询,指EXISTS和NOT EXISTS子查询
--子查询的WHERE子句中支持对父查询的引用
SELECT A
FROM T1
WHERE EXISTS (SELECT B FROM T2 WHERE T1.X = T2Y);
4.8. Common Table Expressions(CTE)
公用表表达式(CTE)是一个临时结果集:该结果集是从 WITH 子句中指定的简单查询派生而来的,紧接在 SELECT 或
INSERT 关键字之前。
CTE 仅在单个语句的执行范围内定义。
CTE 可以在 SELECT, INSERT,CREATE TABLE AS SELECT 或 CREATE VIEW AS SELECT 语句中使用。
就拿这个东西当一个表用就可以。
with q1 as ( select * from student where num= 95002),
q2 as ( select num,name,age from q1)
select * from (select num from g2) a;
with q1 as (select * from student where num = 95002),
q2 as (select * from student where num = 95004)
select * from q1 union all select * from q2;
5. join 链接操作
根据数据库的三范式设计要求和日常工作习惯来说,我们通常不会设计一张大表把所有类型的数据都放在一起,而
是不同类型的数据设计不同的表存储。
比如在设计一个订单数据表的时候,可以将客户编号作为一个个外键和订单表建立相应的关系。而不可以在订单表中 添加关于客户其它信息(比如姓名、所属公司等)的字段。
在这种情况下,有时需要基于多张表查询才能得到最终完整的结果;
join 语法的出现是用于根据两个或多个表中的列之间的关系,从这些表中共同组合查询数据。
5.1. Join 语法规则
在 Hive 中,当下版本 3.1.2 总共支持 6 种 join 语法。分别是:Inner join 内连接、Left join、Right join、full outer join、Left semi join、Cross join。
SELECT a.* FROM a JOIN b ON (a.id = b.id)
SELECT a.* FROM a JOIN b ON (a.id = b.id AND a.departmerht = b.department)
Join 语法丰富,从 Hive0.13.0 开始,支持隐式联接表示法(请参阅 HIVE-5558)。允许 FROM 子句连接以逗号分隔的表列表,而省略 JOIN 关键字。
SELECT
FROM table1 t1, table2 t2, table3 t3
WHERE t1.id = t2.id AND t2.id = t3.id AND t1.zipcode = '025335';
从 Hive2.2.0 开始,支持 ON 子句中的复杂表达式,支持不相等连接(请参阅 HIVE-15211 和 HIVE-15251)。在此之前,Hive 不支持不是相等条件的联接条件。
SELECT a.* FROM a LEFT OUTER JOIN b ON (a.id <> b.id)
5.2. Full outer join 全外连接
5.3. Left semi join 左半开连接
左半开连接(LEFT SEMI JOIN)会返回左边表的记录,前提是是其记录对于右边的表满足 0N 语句中的判定条件。
从效果上来看有点像 inner join 之后只返回左表的结果。