这篇文档是我两年前刚学习Hive的时候写的相当于笔记一样的东西,无意中被我翻出来了。。。
create database|schema [if not exists] xiaoming;
中括号中的if not exists表示如果数据库已经存在就不创建,不存在则创建。
show databases|schemas;
use xiaoming;
drop database [if exists] xiaoming [restrict|cascade];
删除数据库,默认情况下,hive不允许删除含有表的数据库,要先将数据库中的表清空才能drop,否则会报错,可以在语句后面cascade关键字,强制删除一个数据库,默认是restrict,表示有限制的
create table [if not exists] xiaoming01(id int,name string);
此时创建的表没有手动指定分隔符,所以采用hive默认的分隔符/001。
加载数据:
load data [local] inpath 'path' [overwrite] into table xiaoming01;
create external table [if not exists] xiaoming01(id int,name string1) location 'path';
外部表和内部表的区别:
Hive在创建内部表的时候,会将映射为表的数据移动到数据仓库指定的路径下,而创建外部表不会,创建外部表hive只会记录数据所在的路径,不会对数据的位置做任何改变。再删除表的时候,内部表会将元数据和数据一起删除,外部表只会删除元数据,不会删除数据。
装载数据:(此处的path需和创建表的时候的location指点的路径一致)
load data [local] inpath 'path' into table xiaoming01;
分区建表分为2种,一种是单分区,也就是说在表文件夹目录下只有一级文件夹目录。另外一种是多分区,表文件夹下出现多文件夹嵌套模式。
create table xiaoming01(id int,name string) partitioned by(country string);
以上是创建单分区表,以国家为分区字段,注意分区字段一定不能是表中已经存在的字段。
create table xiaoming01(id int,name string) partitioned by(country string,province string);
以上是创建双分区表,以国家为第一分区字段,省份为第二分区字段。
装载数据:
-- 单分区表:
load data [local] inpath 'path' [overwrite] into table xioaming01 partition(country='CN');
-- 双分区表:
load data [local] inpath 'paht' [overwrite] into table xioaming02 partition(country='CN',province='ShangHai');
查看分区表的分区:
show partitions xiaoming01;
基于分区的查询:
select * from xiaoming01 where country = 'CN'; -- 查询CN分区下的数据
desc xiaoming01; -- 查询表结构
注意:
首先,hive在默认情况下是不支持分桶操作的,需要我们手动开启。
set hive.enforce.bucketing = true; -- 开启分桶表
set mapreduce.job.reduces = 4; -- 设置reduce的个数为4,也是最大分桶的个数
create table xiaoming01(id int,name string) clustered by(id) into 4 buckets; -- 创建一个分桶表分桶字段为id,指定4个分桶。
装载数据:(分桶表装载数据不能使用load的方式)
insert overwrite table xiaoming01 select * from student cluster by(id);
分桶表装载数据需要使用insert+select,需要使用一个中间临时表,进行分桶查询,再将查询到的结果插入到分桶表中。(分桶需要经过reduce这一过程,普通的load本质就是hive替我们做了put操作,没有经过MR程序)。
需求: 对某列进行分桶的同时,根据另一列进行排序
insert table xiaoming01 select * from student distribute by(id) sort by(name asc|desc);
在排序的时候不能使用cluster by 和 sort by 进行组合,因为cluster by默认是分桶且排序的,如果再进行排序就会冲突。
cluster by(分桶且排序,同一字段) == distribute by(分桶) + sort by(排序,字段可以不同)
注意:
- 分桶表(分簇表)创建的时候 分桶字段必须是表中已经存储(存在)的字段。
- 分桶表数据采用insert+select装载数据的时候进行了mr程序,插入的分桶数据来自对应的mr程序的partition中。所以默认是采用哈希分桶。
- 分桶表也是把表所映射的结构化数据文件分成更细致的数据,但是更多的是用在join查询上提高效率。
alter table xiaoming01 rename to xiaoming02;
alter table xiaoming01 add columns(dept string comment '部门');
alter table xiaoming01 drop[column] dept;
alter table xiaoming01 change name newName string[first|after column_name];
alter table xiaoming01 replace columns (newName string name string);
alter table xiaoming01 add partition (country='USA') location 'path';
在执行添加分区时,path文件夹下的数据不会被移动。并且没有分区目录country=USA
alter table xiaoming01 add partition(country='USA',province='NewYork') location 'path' partition(country='CN',province='ShangHai') location 'path';
alter table xiaoming01 drop if exists partition(country='USA');
alter table xiaoming01 partition(country='USA') rename to partition(country='CN');
truncate table xiaoming01; -- 删除xiaoming01表的所有数据。
drop table xiaoming01; -- 删除xiaoming01这张表。
使用load装载数据时,Hive不会进行任何转换,加载操作是将数据文件移动到与 Hive 表对应的位置的纯复制/移动
操作。
load data [local] inpath 'path' [overwrite] into table tablename [partition (partcol1=val1, partcol2=val2 ...)] ;
Hive 可以使用 insert 子句将查询结果插入到表中
-- overwrite 关键字会将原本的数据进行覆盖
INSERT OVERWRITE TABLE tablename1 [PARTITION (partcol1=val1, partcol2=val2 ...) [IF NOT EXISTS]] select_statement1 FROM from_statement;
-- into 关键字则是直接导入
INSERT INTO TABLE tablename1 [PARTITION (partcol1=val1, partcol2=val2 ...)] select_statement1 FROM from_statement;
需要保证查询结果列的数目和需要插入数据表格的列数目一致。
如果查询出来的数据类型和插入表格对应的列数据类型不一致,将会进行转换, 但是不能保证转换一定成功,转换失败的数据将会为 NULL。
可以将一个表查询出来的数据插入到原表中, 结果就是相当于复制了一份 cite 表格中的数据。
首先创建三张表,第一张表中的第一个字段和第二张表的字段想同,第二个字段和第三张表的字段相同
create table source_table (id int, name string) row format delimited fields terminated by ',';
create table test_insert1 (id int) row format delimited fields terminated by ',';
create table test_insert2 (name string) row format delimited fields terminated by ',';
下面这条的语句的也是就是从source_table中查询出来id插入到test_insert1表中,将name查询出来插入到test_insert2表中,这样既完成了多重插入,将一个表中的字段分别插入到若干个表中。
from source_table -- 查询 source_table 表
insert overwrite table test_insert1
select id -- 将id字段插入 test_insert1 表中
insert overwrite table test_insert2
select name; -- 将name字段插入 test_insert2 表中
动态分区功能和分桶功能一样都是默认关闭的,我们需要手动开启。
set hive.exec.dynamic.partition=true; -- 是否开启动态分区功能,默认false关闭。
set hive.exec.dynamic.partition.mode=nonstrict; -- 动态分区的模式,默认strict,表示必须指定至少一个分区为静态分区,nonstrict模式表示允许所有的分区字段都可以使用动态分区。
需求: 将dynamic_partition_table中的数据按照时间(day),插入到目标表d_p_t的相应分区中。
-- 创建源数据表
create table dynamic_partition_table(day string,ip string)row format delimited fields terminated by ",";
load data local inpath 'path' into table dynamic_partition_table;
2015-05-10,ip1
2015-05-10,ip2
2015-06-14,ip3
2015-06-14,ip4
2015-06-15,ip1
2015-06-15,ip2
-- 创建导入目标表:
create table d_p_t(ip string) partitioned by (month string,day string);
-- 进行动态插入操作:
insert overwrite table d_p_t partition (month,day)
select ip,substr(day,1,7) as month,day
from dynamic_partition_table;
需求: 查询结果导出到文件系统
-- 将查询结果保存到指定的文件目录(可以是本地,也可以是hdfs)
-- 将t_p表的数据全部查出导入到本地文件中
insert overwrite local directory '/home/hadoop/test'
select * from t_p;
-- 将t_p表的数据全部查出导入到HDFS中
insert overwrite directory '/aaa/test'
select * from t_p;
注意:
多态插入的字段是按位置一一映射的,所以即使是字段名字不一样但是如果位置对应就会插入成功。
基本的 Select 语法结构
select [ all | distinct ] select_expr,select_expr,...from table_name join table_other on expr
[where where_condition]
[group by col_list [hiving condition] ]
[cluster by col_list | [distribute by col_list ]-[sort by | order by col_list] ]
[limit number]
以上语法的顺序不可变!
说明:
1、order by:
会对输入做全局排序,因此只会有一个reduce task,当输入的数据量大时,会导致计算需要较长的时间。
2、sort by:
不是全局排序,会在数据进入reduce task前排序完成,所以sort by只保证每个reduce的输出排序,不保证全局排序。
3、distribute by:
是根据指定字段的数据将数据分到不同的reduce,分发算法是hash散列。
4、cluster by:
除了具有distribute by的功能外,还会对数据指定的字段进行排序。如果分桶和 sort 字段是同一个时,此时,cluster by = distribute by + sort by。
5、distinct:
表示从一个字段中获取不同的值。
内连接:将符合两边连接条件的数据查询出来
select * from t_a a inner join t_b b on a.id=b.id;
左外连接:以左表数据为匹配标准,右边若匹配不上则数据显示null
select * from t_a a left join t_b b on a.id=b.id;
右外连接:与左外连接相反
select * from t_a a right join t_b b on a.id=b.id;
左半连接:左半连接会返回左边表的记录,前提是其记录对于右边表满足on语句中的判定条件。
select * from t_a a left semi join t_b b on a.id=b.id;
全连接(full outer join):返回左右两边所有的数据,匹配不到的先生为null。
select * from t_a a full join t_b b on a.id=b.id;
in/exists关键字(1.2.1之后新特性):效果等同于left semi join
select * from t_a a where a.id in (select id from t_b);
select * from t_a a where exists (select * from t_b b where a.id=b.id);
cross join(##慎用)返回两个表的笛卡尔积结果,不需要指定关联键。
select a.*,b.* from a cross join b;
注意:
- reduce在join时会缓存除了最后一个表的所有表的数据,因此,在开发中,我们应该把最大的表放在最后面,减小内存的缓存。
- hive支持等值join查询,不支持非等值查询,另外,hive支持2张表以上的join。
- join应该在在where语句前面。
- join是不能交换位置的,无论是left还是right join都是左连接的。
当hive内置的函数无发满足我们的实际需求时,我们就可以考虑编写一个自定义函数
了。
自定义函数类别 :
UDF:作用于单个数据行,产生一个数据行作为输出。(数学函数,字符串函数)
UDAF(用户定义聚集函数):接收多个输入数据行,并产生一个输出数据行。(count, max)
如何编写一个UDF程序:
编写java程序,继承UDF类,并重载evaluate方法。
import org.apache.hadoop.hive.ql.exec.UDF;
public class AddUdf extends UDF {
public Integer evaluate(Integer a, Integer b) {
if (null == a || null == b) {
return null;
}
return a + b;
}
public Double evaluate(Double a, Double b) {
if (a == null || b == null)
return null;
return a + b;
}
}
打成jar包上传到服务器
将jar包添加到hive add jar /path/AddUdf.jar;
创建临时函数与开发好的class关联起来
create temporary function add_example as 'xxx.AddUdf';
使用自定义函数 SELECT add_example(scores.math, scores.art) FROM scores;
--销毁临时函数
drop temporary function add_example
A、hive的读文件机制:首先调用inputformat(默认为TextInputFormat)去读取数据,一行一行的读入,然后使用SerDe(默认LazySimpleSerDe)的 Deserializer,将一条记录切分为各个字段(默认分隔符\001
)
所以,hive的默认分隔符是\001
,所以如果我们没有指定分隔符的时候,我们load的文件中的分隔符也需要是\001
,否则程序虽然不会报错,但会识别不出数据,返回null,null,null。。。
B、Hive 对文件中字段的分隔符默认情况下只支持单字节分隔符,如果数据文件中的分隔符是多字符的,如下所示: 01||zhangsan 02||lisi 可用使用 RegexSerDe 通过正则表达式来抽取字段 。
create table t_bi_reg(id string,name string)
row format serde 'org.apache.hadoop.hive.serde2.RegexSerDe'
with serdeproperties(
'input.regex'='(.*)\\|\\|(.*)',
'output.format.string'='%1$s %2$s'
)
stored as textfile;
其中: input.regex:输入的正则表达式表示 || 左右两边任意字符被抽取为一个字段 output.format.string:输出的正则表达式%1 s s %2 ss 则分别表示表中的第一个字段、第二个地段
注意事项:
a、使用 RegexSerDe 类时,所有的字段必须为 string
b、input.regex 里面,以一个匹配组,表示一个字段