前言:
常见hive参数设置:
//Hive 显示列名/表头
set hive.cli.print.header=true;
//hive设置本地资源执行
set hive.exec.mode.local.auto=true;
1、hive的DDL——data define language数据定义语言
1.1库的定义语言
1)创建数据库
create database if not exists 数据库名;
2)切换数据库
use 数据库名;
3)查看所有数据库
show databases;
show databases like "test*"; 所有test开头的数据库
4)查看数据库的描述信息
desc database 数据库名;
desc database extended 数据库名; //查看数据库的扩展信息
5)查看正在使用的数据库
select current_database();
6)修改数据库——不支持
7)删除数据库
drop database [if exists] 数据库名[restrict]; //只能删除空数据库
drop database 数据库名 cascade; //级联删除非空数据库
1.2表的定义语言
1.2.1创建表
CREATE [EXTERNAL] TABLE [IF NOT EXISTS] table_name
[(col_name data_type [COMMENT col_comment], ...)]
[COMMENT table_comment]
[PARTITIONED BY (col_name data_type [COMMENT col_comment], ...)]
[CLUSTERED BY (col_name, col_name, ...)
[SORTED BY (col_name [ASC|DESC], ...)] INTO num_buckets BUCKETS]
[ROW FORMAT row_format]
[STORED AS file_format]
建表语句说明:
①externel hive中的建表关键字
外部表关键字,加上后建的表就是外部表,不加默认是内部表
②if not exists 建表防止报错
if exists 删表防止报错
③comment 指定列或表的描述信息
④partitioned by(字段名 字段类型,…)
分区表用于指定分区用的,括号里的是指定分区字段用的,一般是常用过滤字段
注意:分区字段一定不是建表中字段的字段
⑤clustered by (col_name,col_name,…)
sorted by (col_name [ASC | DSC],…) into num_buckets buckets
指定分桶表相关信息
分桶字段——分桶过程中需要按照哪一个字段将大表切成小文件
clustered by(分桶字段)
into num_buckets buckets 指定分桶个数,每一个桶的数据,分桶字段.hash % 分桶个数
sorted by 指定桶中数据的排序
注意:分桶字段一定包含在建表字段中
⑥row format 指定行格式化
hive不支持update、delete,支持insert,但是效率低
hive擅长导入表中数据方式——load,之间将一个文件导入到hive表中
这里的行格式化指定的是文件每一个字段的切分依据,行之间的切分依据
⑦指定存储格式
指定的是hive表数据在hdfs存储的文件格式
TEXTFILE 文本——默认
SEQUENCEFILE——二进制
RCFILE——行列结合
⑧location 指定hive的表数据在hdfs的存储路径
hive表数据存储路径
- hive-default.xml hive.metastore.warehouse.dir 默认的
- hive-site.xml 修改后的
- location hive表存储路径
这个表必须在hdfs上,在建表过程中指定
1.2.2创建表案例
1)内部表
create table if not exists stu_managed(
sid int,name string,sex string,age int,dept string
) comment "test one managed_table"
row format delimited fields terminated by ","
stored as textfile location "/data/hive/managed/stu";
2)外部表
create external table if not exists stu_external(
sid int,name string,sex string,age int,dept string
) comment "test one external_table"
row format delimited fields terminated by "," ;
3)分区表
分区字段:dept
create table if not exists stu_partitioned(
sid int,name string,sex string,age int
) comment "test one partitioned_table"
partitioned by (dept string) row format delimited fields terminated by "," ;
4)分桶表
分桶字段:age
分桶分数:3
create table if not exists stu_buk(
sid int,name string,sex string,age int,dept string
) clustered by (age) sorted by (dept desc,age asc) into 3 buckets
row format delimited fields terminated by ",";
5)表复制
like
只会复制表的字段信息,不会复制表的属性(存储位置、存储格式、权限)
create [external] table if not exists tbname1 like tbname2;
6)ctas建表
将一个sql查询结果存放到一张表中
create [external] table tbname as select ....
1.2.3查看表列表
show tables;
show tables like "stu_*";
show tables in 数据库名;
查看表的详细描述信息
desc 表名; //查看表字段信息
desc extended 表名; 查看表扩展信息,但是不易读
desc formatted 表名 //查看表扩展信息,格式化显示,易读
1.2.4表修改 alter
1)修改表名
alter table 老表名 rename to 新表名;
2)修改表列信息
- 添加一个列
alter table 表名 add columns(列名 列属性); ——添加到表的最后
- 修改列
alter table 表名change 老列名 新列名 新列属性; ——修改列名,类型不变
alter table 表名change 列名 列名 新属性; ——修改类型,列名不变
注意: 修改列属性,大→小是可以的,由小→大不支持
3)删除列——不支持
4)修改表的分区信息
默认分区表中的分区是0个
①添加一个分区信息
alter table 表名 add partition(dept="IS"); 约束当前分区存储的数据
//添加多个分区
alter table stu_partitioned add partition(dept="IS")
partition(dept="MA") partition(dept="CS");
②修改表的分区信息——了解
修改表的分区的存储位置
分区表的每一个分区,默认的存储位置
/user/myhive/warehouse/test_1901.db/stu_ptn/dept="is"
- 添加分区的时候直接指定这个分区的存储位置
alter table stu_ptn add if not exists partition(dept="aa")
location "/data/hive/ptn/aa";
- 对于已经添加的分区,set location修改分区存储位置
alter table stu_ptn partition(dept="IS")
set location "/data/hive/ptn/is";
这个指定的路径不会立即创建,插入数据的时候才会创建,原来的路径也不会帮我们删除
③查询表的分区信息
show partitions 表名; ——查询表的所有分区
分区字段超过一个就是多级分区,前面的字段是高级分区,后面的是低级分区
show partitions 表名 partition(高级分区); ——查看某一分区下的所有子分区
④删除表的分区信息
alter table 表名 drop if exists partition(分区字段=分区名);
1.2.5清空表
truncate table 表名;
清空表数据,不会删除表,只能内部表使用,删除表目录下的所有文件
1.2.6删除表
drop table if exists 表名;
1.2.7其他辅助命令
show create table 表名; 查看建表语句
2、hive的DML——data manage language数据操作/管理语言
2.1表数据插入
load
将数据从一个文件直接加载到hive的一个表中
LOAD DATA [LOCAL] INPATH 'filepath' [OVERWRITE] INTO TABLE
tablename [PARTITION(partcol1=val1, partcol2=val2 ...)]
语句说明:
1)load data 加载数据;
2)local从本地磁盘加载的关键字,从本地磁盘把数据加载到hive表中;不加local是从hdfs加载数据,从hdfs把数据加载到hive中 ;
3)inpath 文件的存储路径;
4)overwrite 覆盖导入,将原来表中的数据清空,不加 overwrite则追加;
5)partition 指定分区表的数据导入。
注意: 分区表的数据导入一定指定导入到哪一个分区
总结: load就是将数据放在hive表存储的hdfs路径下,hive中表就是hdfs一个路径(目录)的使用者,只要在这个路径下添加文件,就可以被表识别该文件没有限制,关联上以表结构的形式呈现出来
insert
1)单条数据插入
- 每次只插入一条数据
insert into table tbname values(.....);
?:
insert into table stu_copy values(1,"zs","nan",99,"ufo");
实际上hive的语句插入是将插入语句转换 mapreduce任务
过程如下:
先生成一个临时表(当前客户端),最终将这个临时表的数据插入到需要插入表中
2)单重数据插入
- 一次插入多条数据
#直接将本地文件加载到表中
load data local inpath "/home/hadoop/tmpdata/student.txt" into table stu_managed;
#从一个表中进行查询数据,将查询的结果插入到另一个表中
insert into table tbname select ...
?:
insert into table stu_copy select * from stu_managed where age=20;
3)多重数据插入
对一个表扫描一次,将多个结果插入到不同的表中
eg: stu_managed
age=18 age=19 stu_copy
age=18 age=20 stu_external
- 正常的写法:
insert into table stu_copy select * from stu_managed where age=18 or age=19;
insert into table stu_external select * from stu_managed where age=18 or age=20;
上面操作会对原始表 stu_managed扫描两次
- 优化写法:
对同一个表扫描只扫描一次,最终不同的结果插入到不同的表中
from tbname
insert ...select ...where ...
insert ... select ...where...
?:
from stu_managed
insert into stu_copy select * where age=18 or age=19
insert into stu_external select * where age=18 or age=20;
2.2数据导入
2.2.1数据导入——分区表的数据导入
注意:分区表数据无法直接导入,必须指定分区
1)静态分区导入
导入数据的时候是静态指定分区名,分区名导入数据的时候是写死的
缺陷:
一定要足够了解数据有哪些分区,如果数据很大,比如有2T,分区很多的时候这个方式不太适用了
适用:
分区数比较少、分区名固定的情况下,分区表进行读取数据的时候,前面的表字段正常从表对应的文件中读取,分区字段从导入数据的时候指定的分区字段读取 分区字段存储在分区的目录上
①load的方式
可以向分区表导入数据,但是这个时候不会对导入的数据进行检查(本质就是数据文件的移动或复制) 所以这种方式在进行分区表数据导入的时候要慎重,只有当你确定这个数据一定是这个分区的时候才可以这么使用,如果不确定则不可以使用这种方式;
生产上也会使用load方式进行导入数据,一般会按照日期建分区,数据采集一般也会按照日期存储。
②insert方式
先将原始数据导入到一个普通表(非分区表)中,再从这个表结果放到分区表中
- 单重数据插入
insert into table tbname partition (分区字段=分区值) select ... from....
注意:
a、分区表数据 insert插入的时候,select的字段的个数和顺序一定要和分区表的建表字段一致;
b、插入insert或导入load 数据到分区表的时候,分区已经存在直接将数据放在分区目录下,分区不存在则自动创建这个分区。
- 多重数据插入
对原始表扫描一次 最终将数据插入到了多个分区中
from ...
insert ... select ... where ..
insert ... select .... where ...
注意:分区字段在进行查询的时候,按照普通字段查询就可以了
select * from stu_ptn where dept="MA";
只会扫描 /user/myhive/warehouse/test_1901.db/stu_ptn/dept=MA 分区下的数据
select * from stu_ptn where age=18; 全表扫描的
2)动态分区插入方式
根据分区字段的实际值进行动态生成分区名,这种方式插入数据只能使用insert的方式
语法:
insert into table tbname partition(分区字段名) select ... from ...
分区字段对应的值是根据select查询的数据来的,所以select中需要将分区字段查询出来
insert into table stu_ptn partition(dept)
select sid,name,sex,age,dept from stu_copy;
此时会报错:
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;
注意: 分区表在建表的时候,会将分区字段放在最后
补充说明:
多级分区的时候,分区级别超过1级,分区字段的个数超过1个一般会按照日期建分区year/month/day;分区字段超过一个按照分区字段的顺序划分级别,前面的字段的级别高于后面的字段的级别
craete ... partitioned by(dept string,age int)...
分区:先按照dept分区,再按照age进行分区,最终目录是stu_ptn02/dept=../age=..
2.2.2导入数据实例
1)两个分区都是静态分区
load和insert 均可,以insert为例
insert into table stu_ptn02 partition(dept="CS",age=18)
select sid,name,sex from stu_copy where dept="CS" and age=18;
2)一静一动
注意: 静态分区必须是高级分区dept,高级分区必须是静态分区,低级分区为动态分区,否则语法报错
insert
insert into table stu_ptn02 partition(dept="MA",age)
select sid,name,sex,age from stu_copy where dept="MA";
#目录结构如下:
/user/myhive/warehouse/test_1901.db/stu_ptn02/dept=MA/age=17
3)两个分区都是动态分区
insert
insert into table stu_ptn02 partition(dept,age)
select sid,name,sex,dept,age from stu_copy;
2.2.2数据导入——分桶表的数据导入
1)load 方式
将表数据分成 3个文件存储
分桶依据: 分桶字段.hash % 分桶个数 ,0 1 2 针对每一条数据进行判断
原则上load方式不支持
load data local inpath “/home/hadoop/tmpdata/student.txt” into table stu_buk;
不支持load的 只能insert
2)insert方式
分桶表插入数据:设置参数
set hive.strict.checks.bucketing=false;
set hive.mapred.mode=nonstrict;
insert into table stu_buk select * from stu_copy;
Number of reducers (= 3) is more than 1 默认设置reducetask的个数就是3个 == 桶的个数的
查询表stu_copy数据导入stu_buk的时候,会根据建表语句中指定的分桶字段分桶,分桶个数将数据分成3个
总结:
- 分桶表 只能用insert方式
- 分桶规则
如果分桶字段整型,分桶字段 % 分桶个数;不是整型,分桶字段.hash % 分桶个数,余数相同的数据到同一个桶中
2.3数据导出
hive中提供将一个查询结果导出为一个文件
2.3.1单重数据导出
INSERT OVERWRITE [LOCAL] DIRECTORY 文件夹(本地的|hdfs) select_statemen
参数解释:
local 导出数据到本地
案例:
#本地:
insert overwrite local directory "/home/hadoop/hive_data"
select * from stu_copy where age=18;
#hdfs:
insert overwrite directory "/home/hadoop/hive_data"
select * from stu_copy where age=18;
2.3.2多重数据导出
扫描一次表,将不同的结果导出不同的目录下
from ...
insert ...
insert ....
案例:
from stu_copy
insert overwrite local directory "/home/hadoop/hive/age18"
select * where age=18
insert overwrite local directory "/home/hadoop/hive/age19"
select * where age=19;
3、hive的DML之查询
语法顺序:
join group by order by limit where having
select .... from ... join...on...where...
group by...having....order by....limit ...
3.1join
注意:
- join 支持等值,不支持非等值
- 支持and连接 不支持or连接 多个连接条件的时候
select * from a join b on a.id=b.id and a.name=b.name; 支持
select * from a join b on a.id=b.id or a.name=b.name; 不支持 mapkey不好设计
- 支持超过2个表的连接
3.1.1join的分类
3.1.1.1内连接 inner join | join
求两个表的连接键的交集,两个表都有返回
3.1.1.2外连接 outer join
左外 left outer join == left join
join 左侧表作为基础表,左侧表有的则返回,没有的不要了,左有右没有就补null
select * from a left join b on a.id=b.id;
结果
1 zs 1 23
2 ls 2 45
4 ww NULL NULL
3.1.1.3右外 right outer join == right join
join 右侧表作为基础表,右表有的则返回,没有的不要了,右有左没有就补null
select * from a right join b on a.id=b.id;
结果:
1 zs 1 23
2 ls 2 45
NULL NULL 3 56
3.1.1.4全外 full outer join == full join
求两个表的并集,两个表中有的所有的关联建都会返回,哪一个对应的关联建有数据,补充数据,没有数据补null
select * from a full join b on a.id=b.id;
结果:
1 zs 1 23
2 ls 2 45
NULL NULL 3 56
4 ww NULL NULL
3.1.1.5半连接 semi join
- 左半连接 left semi join
mysql in/exits 判断字段值是否包含在我们给定的范围中
select *
from a where id in (select id from b);
hive2中这个语句可以执行但是执行效率低 mapkey hive1中不支持
半连接就是高效解决 in /exists 问题
select * from a left semi join b on a.id=b.id;
最终取的结果a b进行内连接,取的是左半表;
最终返回的是a表中在b表中出现过的a所有的数据。
另一种实现方式:
select a.* from a join b on a.id=b.id;
内连接取左部分的表
3.2group by
group by后面的字段相同的分到一组中
注意:group by 使用的时候是有很大的限制的
3.2.1使用group by时select后面的字段(需要查询的字段)有严格的限制
只能跟两种类型的字段
1)group by的分组字段
select dept from stu_copy group by dept;
2)根据分组字段的进行的聚合函数
聚合函数 多–>一条,如sum max avg min…
select dept,max(age) from stu_copy group by dept;
错误示范:
select dept,sid,max(age) from stu_copy group by dept;
3.2.2group by 的执行顺序在select 执行之前 group by中不能使用select中字段的别名
错误的
select dept d,max(age) from stu_copy group by d;
正确的:
select dept d,max(age) from stu_copy group by dept;
3.3where和having
where:
where是对聚合函数之前的数据进行过滤的,where就是为聚合函数准备数据,需要聚合的数据先进行一步过滤再聚合。
having:
执行顺序是在聚合函数之后,针对聚合之后的结果进行过滤。
3.4order by
注意: order by 的执行顺序在select之后,可以使用select中的别名
在hive中order by这个位置4个语法:
- order by 用于全局排序,无论启动多少个reducetask最终全局有序
- sort by 用于局部排序,每一个reducetask的运行结果有序的,不保证全局有序
- distribute by 用于字段分配
- reducetask的个数需要手动指定 set mapreduce.job.reduces=2;
按照指定的字段进行分配reducetask的数据 - distribute by 分+ sort by 排序
需要指定字段进行分 reducetask (mapreduce 分区) 指定字段排序
先要按照性别分成两个reducetask,再在每一个reducetask中按照年龄排序
select * from stu_copy distribute by sex sort by age;
划分依据: sex .hash % reducetasks
cluster by 用于先按照指定字段切分reducetask的数据再按照指定字段进行升序排序=distribute by 字段 + sort by 字段
调整 reducetask的个数:
set mapreduce.job.reduces=2;
当distribute by 的字段和sort by的字段一致的时候,并且升序可以使用cluster by 替换
按照age 切分,按照age升序
select * from stu_copy cluster by age;
select * from stu_copy distribute by age sort by age;
3.5limit
全局前几个,所有数据的前几个
select * from stu_copy limit 1;
补充:hql语句转换为mr时候日志中显示和解释
In order to change the average load for a reducer (in bytes):
每一个reducer平均加载的字节数
set hive.exec.reducers.bytes.per.reducer=<number>
In order to limit the maximum number of reducers:
reducetask最大限制
set hive.exec.reducers.max=<number> 1009
In order to set a constant number of reducers:
设置reducetask的个数的
set mapreduce.job.reduces=<number> -1
0 没有reducetask 需要的时候1
特例 分桶表插入数据的时候 reducetask=== 桶的个数