分区表
创建表的时候可以使用 PARTITIONED BY 来创建分区表。分区表实际上就是对应一个 HDFS 文件系统上的独立的文件夹,该文件夹下是该分区所有的数据文件。Hive 中的分区就是分目录,把一个大的数据集根据业务需要分割成小的数据集。在查询时通过 WHERE 子句中的表达式选择查询所需要的指定的分区,这样的查询效率会提高很多。
1 | CREATE [EXTERNAL] TABLE [IF NOT EXISTS] table_name |
分区表的创建方式
直接使用 create 创建分区表
1 | create external table if not exists default.deptpart1( |
创建数据
partition_data1
1 | 1 测试 1 |
partition_data2
1 | 5 测试 5 |
加载数据到分区表中
1 | load data local inpath '/home/rexyan/test_file/partition_data1' into table default.deptpart1 partition(area='shanghai'); |
上面命令会将数据放在不同的分区中,partition_data1 的数据放在分区 area=’shanghai’ 中,partition_data2 的数据放在 area=’beijing’ 中。查询数据时会发现字段中默认增加了一列 area,area 的值就是分区的值。可以使用where 条件过滤该列的值。在 hdfs 中,会发现 deptpart1 目录下有 area=’shanghai’ 和 area=’beijing’ 两个分区目录。
1 | hive (default)> select * from deptpart1; |
使用 alter 增加分区字段
创建普通表后或者在目前分区表的基础上使用 alter 增加分区字段
1 | hive (default)> alter table deptpart1 add partition(area="shandong"); |
1 | hive (default)> show partitions deptpart1; # 查看添加的分区 |
使用 load 直接加载数据
使用 load 命令向分区加载数据,如果分区不存在,load 时自动帮我们生成分区
创建一张分区表
1 | create external table if not exists default.deptpart2( |
使用 load 命令加载数据,将加载时指定分区,因为上面刚创建的表是没有分区 area=’jiangshu’ 和 area=’wuhan’ 的,使用下面方式导入后就能直接创建分区,并将数据导入。
1 | load data local inpath '/home/rexyan/test_file/partition_data1' into table default.deptpart2 partition(area='jiangshu'); |
修复分区命令
创建一张分区表,直接将数据上传到 hdfs 目录下,这时使用 hive 是查询不了数的,需要使用命令修复分区自动生成分区的元数据。
创建分区表
1 | create external table if not exists default.deptpart3( |
上传数据到 hdfs
1 | [rexyan@hadoop10 ~]$ hadoop fs -mkdir -p /hive/deptpart3/area=guiyang |
修复分区
1 | hive (default)> msck repair table deptpart3; # 修复分区 |
分桶表
建表时指定了 CLUSTERED BY,这个表就为分桶表,分桶本质上也是为了分散数据,在分桶后,可以结合 hive 提供的抽样查询,只查询指定桶的数据。在分桶时,也可以指定将每个桶的数据根据一定的规则来排序,如果需要排序,那么可以在 CLUSTERED BY 后根 SORTED BY
创建分桶表
1 | create table stu_buck(id int, name string) |
向分桶表导入数据时,必须运行 MR 程序,才能实现分桶操作,之前使用的 load 操作只是相当于执行的 put 操作,无法满足分桶表导入数据,必须使用 insert into (hive 会自动转换为 MR 程序运行)。
在导入数据前还需要打开强制分桶开关和强制排序开关
1 | 打开强制分桶开关: set hive.enforce.bucketing=true; |
插入数据除了使用 insert into 一条条的插入外,还可以使用查询插入的方式,即查询一张表的结果,并将这正表的数据插入到分桶表中。这里使用查询插入的方式实现。
1 | create table stu_buck_tmp(id int, name string) -- 创建普通表 stu_buck_tmp |
将数据加载到表 stu_buck_tmp 中
1 | [rexyan@hadoop10 test_file]$ hadoop fs -put ~/test_file/stu_buck_tmp_data /hive/stu_buck_tmp |
将数据从 stu_buck_tmp 中查出插入到 stu_buck 中
1 | insert into table stu_buck select * from stu_buck_tmp; |
因为 stu_buck 表分桶数量是 4, 所以会看到在插入数据的时候有 4 个 reduce task 执行,查看 hdfs 结构会看到在 stu_buck 下有四个数据文件,每个代表一个桶,每个里面存放一部分数据,且数据都是按照 hash 取模的方式分配的,且数据是倒序存在的。
抽样查询
抽样查询的表必须是分桶表,语法格式为 select * from 分桶表 tablesample(bucket x out of y on 分桶表分桶字段);
以上面创建的分桶表为例,查询示例如下
1 | select * from stu_buck tablesample(bucket 1 out of 2 on id); --代表从第1桶(0号桶)开始抽,每隔2桶抽一次,一共抽2桶(4/2,4为总桶数,2为每次抽取桶的间隔数)。即最后抽取的桶为 0号桶,1号桶 |
数据导入
load
将数据直接加载到表目录中,语法:load data [local] inpath 路径 into table 表名 partition(xx=xx)
,local 参数的作用是将本地文件系统的文件上传到 hdfs 中。不加 local 代表着,文件在 hdfs 上,并且将文件从 hdfs 上将源文件移动到目标目录。
insert
insert 的方式会运行 MR 程序,通过程序将数据输出到表目录。在某些场景, 必须使用 insert 的方式来导入数据:
- 向分桶表插入数据
- 如果指定表中的数据,不是以纯文本(TextFile)形式存储,需要使用 insert 方式导入
语法:insert into/overwrite table 表名 select xxx/values(),(),()
可以使用 insert into 或者 insert overwrite 两种方式来插入数据,insert into 代表向表中追加新的数据,insert overwrite 代表先清空表中所有的数据,再向表中添加新的数据。后面数据的来源方式可以自己写在 values 中,也可以使用 select 来将查询结果进行插入。
import
不仅可以导入数据还可以顺便导入元数据(表结构)。Import 只能导入 export 导出的内容。
语法格式为 import external table 表名 from HDFS路径
,使用 import 导入要遵循这些约束,如果向一个新表中导入数据,hive 会根据要导入表的元数据自动创建表。如果向一个已经存在的表导入数据,在导入之前会先检查表的结构和属性是否一致,只有在表的结构和属性一致时,才会执行导入。不管表是否为空,要导入的分区必须是不存在的。
数据导出
insert
将一条 sql 运算的结果,插入到指定的路径,语法格式为 insert overwrite [local] directory 目录 row format 格式
。这里 local 的含义和导入的时候一样,不加 local 代表着将文件导出到 hdfs 上,加上则是导出到文件系统中。
export
导出数据和元数据(表结构),export 会在 hdfs 的导出目录中,生成数据和元数据,并且导出的元数据是和 RDMS无关的。如果是分区表,可以选择将分区表的部分分区进行导出
语法格式为 export table 表名 [partiton(分区信息) ] to HDFS路径
1 | export table deptpart1 to "/export"; -- 导出 deptpart1 |
1 | import table deptpart1_import from "/export"; -- 导入 deptpart1_import |
元数据信息还在
1 | hive (default)> show partitions deptpart1_import; |
排序
Hive 的本质是 MR,MR 中的排序有以下几种:
- 全排序: 结果只有一个(只有一个分区),所有的数据整体有序
- 部分排序: 结果有多个(有多个分区),每个分区内部有序
- 二次排序: 在排序时,比较的条件有多个
在 MR 中,排序在 reduce 之前就已经排好序了,排序是 shuffle 阶段的主要工作。分区是指使用 Partitioner 来进行分区,当 reduceTaskNum>1,使用用户自己定义的分区器,如果没有就使用 HashParitioner,HashParitioner 只根据 key 的 hashcode 来分区。
Hive 中可以使用以下排序:
- ORDER BY 列名: 全排序
- SORT BY 列名: 部分排序,如果希望自定定义使用哪个字段分区,需要使用 DISTRIBUTE BY
- DISTRIBUTE BY 列名: 指定按照哪个字段分区, 结合 sort by 使用
- CLUSTER BY 列名: 如果分区的字段和排序的字段一致,且是正序排序,那么可以用 CLUSTER BY,即
DISTRIBUTE BY 列名 sort by 列名 asc
等价于CLUSTER BY 列名
, CLUSTER BY 后不能写排序方式,只能使用默认的按照 asc 排序。
操作实例:
创建表 emp1,并插入数据,数据如下
1 | create table default.emp1( |
1 | 7369 SMITH CLERK 7902 1980-12-17 800.00 |
ORDER BY 查询示例
1 | select * from empno order by empno desc; |
结果显示如下
1 | emp1.empno emp1.ename emp1.job emp1.mgr emp1.hiredate emp1.sal emp1.comm emp1.deptno |
SORT BY 和 DISTRIBUTE BY 查询示例
1 | set mapreduce.job.reduces=3; -- 设置 reduce task 的数量为 3 |
1 | insert overwrite local directory "/tmp/orderby" ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' select * from emp1 distribute by mgr sort by empno desc; |
上面 HQL 的意思是对 empno 进行降序排序,并且按照 mgr 字段进行分区【如果不加 distribute by,那么就不知道会对哪个字段进行分区】,mgr 字段是 int 类型的,所以会将该列的值 %3 进行分区,所以结果文件为 3 个。因为 mgr 这一列的值 %3 都等于 0,所以只有第一个分区(分区0)有值,其他的两个分区都是空的。
CLUSTER BY 查询示例
1 | set mapreduce.job.reduces=3; -- 设置 reduce task 的数量为 3 |
1 | insert overwrite local directory "/tmp/orderby" ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' select * from emp1 cluster by empno; |
cluster by empno
相当于 distribute by empno sort by empno desc
函数
函数有所属库的概念,系统提供的除外。系统提供的函数可以在任意库中使用。
1 | 查看当前库所有的函数:show functions; |
函数的来源
- 系统函数,自带的,直接使用即可
- 用户自定义的函数
函数按照特征分类
- UDF: 用户定义的函数。 一进一出。 输入单个参数,返回单个结果
- UDTF: 用户定义的表生成函数。 一进多出。传入一个参数(集合类型),返回一个结果集
- UDAF: 用户定义的聚集函数。 多进一出。 传入一列多行的数据,返回一个结果(一列一行)
常用日期函数(hive 默认解析的日期格式必须是: 2019-11-24 08:09:10)
1 | unix_timestamp:返回当前或指定时间的时间戳 |
常用取整函数
1 | round: 四舍五入 |
常用字符串操作函数
1 | upper: 转大写 |
集合操作
1 | size: 集合(map和list)中元素的个数 |
查询语句和 MySQL 的不同点
1 | A <=> B : ①A,B都为null,返回true |