Hive sql是Hive 用户使用Hive的主要工具。Hive SQL是相似于ANSI SQL标准的SQL语言,可是二者有不彻底相同。Hive SQL和Mysql的SQL方言最为接近,可是二者之间也存在着显著的差别,好比Hive不支持行级数据的插入、更新和删除,也不支持事务操做。java
注: HIVE 2.*版本以后开始支持事务功能,以及对单条数据的插入更新等操做sql
Hive中的数据库从本质上来讲仅仅就是一个目录或者命名空间,可是对于具备不少用户和组的集群来讲,这个概念很是有用。首先,这样能够避免表命名冲突;其次,它等同于与关系型数据库中数据库的概念,是一组表或者表的逻辑组,很是容易理解数据库
Hive中的表和关系型数据库中table概念是相似的,每一个table在Hive中都有一个相应的目录存储数据。若是说,你没有指定表的数据库,那么Hive会经过{HIVE_HOME}/conf/hive_site.xml配置文件中的hive.metastore.warehouse.dir属性来使用默认值(通常是/usr/hive/warehouse,也能够根据实际状况来进行修改该配置),全部的table都保存在这个目录中。ide
Hive中的表分为两类,分别为内部表和外部表工具
内部表,也即Hive管理的表,Hive内部表的管理包括逻辑以及语法上的,也包含实际物理意义上的,也就是说,建立Hive内部表后,表中的数据其实是存储在表所在的目录内,由Hive自己来管理,什么意思呢?也就是说,若是你想删除表的话,那么,连同表的物理数据,元数据等会一并删除。举个栗子:oop
create table managed_table(name string,age int); load data inpath '/hadoop/guozy/data/user.txt' into table managed_table;
第一条语句,建立一张简单的内部表,测试
第二条语句,将hdfs://hadoop/guozy/data/user.tx 移动到Hive对应的目录hdfs://user/hive/warehouse/managed_table/这个目录中。注意,这里是移动,并不是复制spa
移动数据是很是快的,由于Hive不会对数据是否符合定义的Schema作校验,这个工做一般在读取的时候进行(即Schema on Read),此时咱们在执行删除操做:code
drop table managed_table;
在执行这条语句以后,其物理数据和表的元数据都会被删除。 orm
相对于内部表来讲,其管理仅仅是在逻辑和语法意义上的,实际的数据并不是由Hive自己来管理,而是交给了HDFS。当建立一个外部表的时候,仅仅是指向一个外部目录而已。若是你想删除表,只是删除表的元数据信息,并不会对实际的物理数据进行删除。举个栗子:
create external table external_table (name string,age int) location '/hadoop/guozy/external_table'; load data inpath '/hadoop/guozy/data/user.txt' into table external_table;
第一条语句,建立一张简单的外部表,这里与内部表的区别是,添加了关键字external和location数据位置
第二条语句,向表中载入数据,会将hdfs://hadoop/guozy/data/user.tx 移动到Hive对应的目录hdfs://hadoop/guozy/external_table这个目录中
对于Hive来讲,它不会校验外部表的数据目录是否存在。因此咱们彻底能够在建立表以后在建立数据。此时咱们在来删除该表:
drop table external_table;
执行上面这条语句以后,Hive删除的仅仅是该表对应的元数据而已,并不会对实际的屋里数据进行删除,也就是说hdfs://hadoop/guozy/external_table这个目录下的数据不会被删除。
Hive将表划分为分区(partition)表和分桶(bucket)表。
分区可让数据的部分查询变得更快,也就是说,在加载数据的时候能够指定加载某一部分数据,并非全量的数据。
分桶表一般是在原始数据中加入一些额外的结构,这些结构能够用于高效的查询,例如,基于ID的分桶可使得用户的查询很是的块。
所谓的分区表,指的就是将数据按照表中的某一个字段进行统一归类,并存储在表中的不一样的位置,也就是说,一个分区就是一类,这一类的数据对应到hdfs存储上就是对应一个目录。当咱们须要进行处理的时候,能够经过分区进行过滤,从而只去部分数据,而不必取所有数据进行过滤,从而提高数据的处理效率。且分区表是能够分层级建立。
分区表又分为静态分区表和动态分区表两种:
hive> create table enter_country_people(id int,name string,cardNum string) partitioned by (enter_date string,country string);
指定分区表关键字:partitioned by
这里的分区字段为:enter_date、country,也就是说,先按照enter_date进行分类,在enter_date的基础上,在按照country再次进行分类
注意,这里的分区字段不能包含在表定义字段中,由于在向表中load数据的时候,须要手动指定该字段的值。
接下来向表中载入数据,而且指定分区为enter_date='2019-01-02',country='china'
hive> load data inpath '/hadoop/guozy/data/enter__china_people' into table enter_country_people partition (enter_date='2019-01-02',country='china');
这样建立表以后的表目录结构是这样的:
这里还有一个问题就是,涉及到载入数据的方式
一、使用的是load命令,也就是我上面的方式,能够看到,在load数据以前,表中是没有这个分区(enter_date='2019-01-02',country='china')的。当执行了load命令以后,hive会自动建立该分区。这只是其中的一种方式,还有一种方式就是,咱们直接能够经过移动数据到该分区的目录下
二、直接经过hdfs的mv命令移动数据到该分区指定的目录下。由于前面说过,所谓的分区只是对应到hdfs存储中的一个目录而已。最终数据查询仍是要到这个目录中去进行查询数据。可是这种方式有个前提就是,该分区所在的目录必须呀提早存在,注意,这里说的是对应的该目录存在。固然这个目录你能够手动mkdir,也能够经过hive添加分区的方式进行建立,这里又分为两种状况:
a.经过hive添加分区的方式进行建立,例如:
hive> alter table enter_country_people add if not exists partition (enter_date='2019-01-03',country='US');
经过这种方式添加分区以后,会生成这样一个目录:hdfs://user/hive/warehouse/2019-01-03/US,此时,咱们就能够直接使用hdfs的mv或cp命令将数据搂到该目录下。以后使用hive命令进行查询便可
b.第二种方式就是,咱们先手动建立该目录:hdfs dfs -mkdir /user/hive/warehouse/2019-01-03/US,而后一样使用上面这种方式,将数据mv或cp到该目录下,可是,若是只是这样的话,你去使用hive命令查询数据,发现查不到,为何,由于hive查询数据是须要先到元数据表中找到对应数据的分区索引的,而后根据找到的分区索引,再去对应的目录中查找,可是才是咱们根本没有对hive的元数据进行操做,因此元数据中没有这个分区的信息,因此此时,咱们须要在增长一步操做,就是将该分区的信息添加到元数据库中,咱们使用hive的分区修复命令便可:
hive> msck repair table enter_country_people;
执行上述命令以后,而后在进行数据查询,就没有什么问题了
对于动态分区表数据的加载,咱们须要先开启hive的非严格模式,而且经过insert的方式进行加载数据
hive> set hive.exec.dynamic.partition.mode=nonstrict; hive> insert into table enter_country_people(user string,age int) partition(enter_date,country) select user,age,enter_date,country from enter_country_people_bak;
注意:一、必须先开启动态分区模式为非严格模式
二、这里在指定分区的时候,并无指定具体分区的值,而只是指定的分区的字段
三、partition中的字段实际上是做为插入目标表中的一个字段,因此在从另一张表select的时候必须查询字段中包含索要分区的这个字段。
在表或者分区中使用分桶一般有两个缘由,一个是为了高效的查询,另外一个则是为了高效的抽样。
桶实际上是在表中加入了特殊的结构,hive在查询的时候能够利用这些结构来提升查询效率。好比,若是两个表根据相同的字段进行分桶,则在对这两个表进行关联的时候可使用map-side关联高效实现。前提是,关联的字段在分桶字段中出现才能够。先看下hive的分桶建表语句:
hive> create table user_bucket(id int comment 'ID',name string comment '姓名',age int comment '年龄') comment '测试分桶' clustered by (id) sorted by (id) into 4 buckets row format delimited fields terminated by '\t';
上述语句,指定根据id字段进行分桶,而且分为4个桶,而且每一个桶内按照id字段升序排序,若是不加sorted by,则桶内不通过排序的,具体的分桶规则是怎样的呢?Hive是根据指定的分桶字段,上述语句中为id,根据id进行hash以后在对分桶数量4进行取余来决定该数据存放在哪一个桶中,所以每一个桶都是总体数据的随机抽样。
在map-side关联操做中,两个表若是根据相同的字段进行分桶,在处理左表的bucket是,能够直接从外表对应的bucket中提取数据进行关联操做。map-side关联的两个表不必定须要彻底相同的bucket数量,只要成倍数便可。一样,Hive不会对数据是否知足表定义中的分桶进行校验,只有在查询时出现异常才会报错,因此通常,咱们将分桶的工做交给Hive本身来完成(设置hive.enforce.bucketing=true).
载入数据:
在载入数据的时候,须要注意一下,若是说咱们只是单纯的使用load语句进行将数据载入到表中的话,实际上是没有任何的分桶效果的,由于这样hdfs文件只有一个,像这样:
hive> load data inpath '/hadoop/guozy/data/user.txt' into table user_bucket;
此时,咱们须要借助一个中间表,先将数据load到中间表中,而后经过insert的方式来向分桶表中载入数据:
hive> create table tmp_table (id int comment 'ID',name string comment '名字',age int comment '年龄') comment '测试分桶中间表' ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' ; hive> load data inpath '/hadoop/guoxb/data/user.txt' into table tmp_table; hive> insert into user_bucket select * from tmp_table;
这样就实现了分桶的效果,注意,分桶和分区的区别,分区体如今hdfs上的文件目录,而分桶则提如今hdfs是具体的文件,上述的语句中,最终会在hdfs上生成四个文件,而不是四个目录,若是当在次向该分桶表中insert数据后,会又增长4个文件,而不是在原来的文件上进行追加。
通常状况下,建表分桶表的时候,咱们都须要指定一下排序字段,这样有一个好处就是,在每一个桶进行链接查询时,就变成了高效的归并排序了。