Hive中数据库的操作
·查看数据库列表
hive(default)> show databases;
·选择数据库
hive(default)> use default;
default是默认数据库,默认就在这个库里面 hive的数据都是存储在hdfs上,那这里的default数据库在HDFS上是如何体现的?
在hive-site.xml中有一个参数hive.metastore.warehouse.dir
<property>
<name>hive.metastore.warehouse.dir</name>
<value>/user/hive/warehouse</value>
<description>location of default for the warehouse</description>
</property>
它的默认值是/user/hive/warehouse,表示hive的default默认数据库对应的hdfs存储目录。 HDFS上确实有这个目录,并且这个目录下还有一个t1目录,其实这个t1就是我们前面在default数据库中创建的那个t1表,从这可以看出来,hive中的数据库和hive中的表,在hdfs上面的体现其实都是目录。 
这个默认数据库的信息在Metastore中也有记录,在dbs表中 
? 创建数据库
hive (default)> create database mydb1;

到metastore中确认一下 
在创建数据库的时候通过location来指定hdfs目录的位置
hive (default)> create database mydb2 location '/user/hive/mydb2';
到HDFS上确认一下 
? 删除数据库
hive (default)>drop database mydb1;
注意:default默认数据库无法删除!
Hive中表的操作
·创建表
hive (default)> create table t2(id int);
·查看表信息
hive (default)> show tables;
·查看表结构信息
hive (default)> desc t2;
·查看表的创建信息
hive (default)> show create table t2;
从这里的location可以看到这个表在hdfs上的位置。 注意了:表中的数据是存储在hdfs中的,但是表的名称、字段信息是存储在metastore中的 到metastore中看一下 先看tbls表,这个里面中存储的都是在hive中创建的表 可以看到DB_ID 为1 可以到dbs表中看到默认default数据库的id就是1。 TBL_NAME 是这个表的名称。 
在表COLUMNS_V2中存储的是Hive表的字段信息(包含字段注释、字段名称、字段类型、字段顺序) 其中的CD_ID和tbls中的TBL_ID相等

·修改表名
hive (default)> alter table t2 rename to t2_bak;

·加载数据 咱们前面向表中添加数据是使用的insert命令,其实使用insert向表里面添加数据只是在测试的时候使用,实际中向表里面添加数据很少使用insert命令的 具体原因我们后面再分析
向表中加载数据可以使用load命令 以t2_bak为例,在bigdata04机器的/data/soft/hivedata下有一个t2.data文件,将其加载到t2_bak表中
 查看表中的内容 
我们到hdfs上去看一下这个表,发现刚才的文件其实就是上传到了t2_bak目录中 
也可以手工通过put命令把数据上传到t2_bak目录中
再查询一下这个表的数据,可以发现数据多了一份,说明刚才使用hdfs的put命令上传的是可以的。  ? 表增加字段及注释、删除表
在工作中会有给已存在的表增加字段的需求,需要使用alter命令 在这里我们给t2_bak表增加一个name字段,重新查看表结构信息,再查询一下这个表中的数据,结果发现,第二列为null,这是正常的,因为我们的数据数据文件中就只有一列,第二列查询不到,就显示为null,不会报错,这一点要注意一下。 
现在我们通过desc查询表中的字段信息发现都没有注释,所以想要给字段加一些注释,以及表本身也可以增加注释,都是使用comment关键字 重新创建一个表t2 注意:在建表语句中,缩进不要使用tab制表符,否则拷贝到hive命令行下执行会提示语句错误,这里的缩进需要使用空格。 
查看这个表的信息,结果发现添加的中文注释都是乱码 
原因是什么? 中文乱码的原因是因为hive数据库里面的表都是latin1编码的,中文本来就会显示乱码,但是又不能修改整个数据库里面所有表的编码,否则在使用hive的时候会出问题,那么只有考虑把存储字段注释和表注释相关的表的编码改为utf8。
登陆Mysql数据库切换到Hive库: 
确认一下现在表COLUMNS_V2和TABLE_PARAMS的编码,都是latin1  
修改这两张表的编码即可;  这样修改之后以后就可以看到中文注释了。
注意:需要先把之前创建的t2表删除掉,因为之前存储的中文已经是乱码了,无法恢复,删除之后重新创建就可以了。
删除表使用drop命令  
? 指定列和行分隔符的指定 在我们实际实践中,肯定不会像上面一样创建一张非常简单的表,实际中表的字段会比较多,下面我们就来创建一个多字段的表t3 
这样创建没有问题,我们来加载对应的数据文件/data/soft/hivedata/t3.data,表中的多列内容之间是使用制表符分割的 看一下表中的数据 
建表、加载数据、查询数据  在这里发现不是我们想要的结果,都是NULL,说明数据没有被识别,这是为什么?
注意了,hive在创建表的时候,需要我们指定相应的行分隔符,列分隔符。而我们在创建mysql表的时候,这些都是不需要的,因为它在组织数据的时候,已经规定好了数据的表现形式。
在创建t3的时候没有指定相应的分隔符,所以导致使用制表符分割的数据无法被解析。
实际上呢,hive是有默认的分隔符的,默认的行分隔符是’\n’,就是换行符,而默认的列分隔符呢,是\001。 \001这个是ASCII码中的特殊不常使用的不可见字符,在文本中可以通过ctrl+v和ctrl+a来输入\001,这里我们在将t3.data改一下,重新上传,再查看表t3。
修改t3.data 
重新加载数据,查询表数据,这个时候发现刚才修改的那条数据被成功解析了 
为了能够在上传数据之后让hive表正确识别数据,那我们该如何修改hive表的默认分隔符呢? 其实也是非常的简单,只要我们在创建表的时候指定一下分隔符就可以了,我们把建表语句修改一下,重新创建一个表t3_new

在这需要注意的是,lines terminated by 行分隔符可以忽略不写,但是如果要写的话,只能写到最后面! 把t3.data文件中的字段分隔符都恢复为制表符,  然后重新把数据加载到t3_new表中。 
查看t3_new表中的数据,注意,针对无法识别的数据显示为NULL,因为最后一列为boolean类型,但是在数据中我故意指定了一个数字,所以导致无法解析,但是不会导致数据加载失败,也不会导致查询失败,这就是hive的特性,他不会提前检查数据,只有在使用的时候才会检查数据,如果数据有问题就显示为null,也不报错。

Hive中的数据类型
hive作为一个类似数据库的框架,也有自己的数据类型,便于存储、统计、分析。 Hive中主要包含两大数据类型 ? 一类是基本数据类型 ? 一类是复合数据类型 基本数据类型:常用的有INT,STRING,BOOLEAN,DOUBLE等 复合数据类型:常用的有ARRAY,MAP,STRUCT等
基本数据类型
看这个表,一般数字类型我们可以试验int,小数可以使用double,日期可以使用date类型、还有就是boolean类型,这些算是比较常见的了,前面我们在建表的时候基本都用过了。 
复合数据类型
下面主要看一下复合数据类型,主要分析:array,map和struct 
Array
先来看Array,这个表示是一个数组结构 在这里举一个例子:学生有多个爱好,有两个学生,zhangsan、lisi, zhangsan的爱好是swing、sing、coding lisi的爱好是music、football 每个学生的爱好都是不固定的,有多有少,如果根据学生的每一个爱好都在表里面增加一列,这样就不合适了,后期可能要经常增加列存储不同的爱好 如果我们如果把每个学生的爱好都拼接为一个字符串保存到一个字段中,这样针对存储层面来说是没有问题的,但是后期需要根据爱好的增加而修改字段,这样操作起来很不方便,如果想获取每个学生的1个爱好,这样是没办法直接获取的,因为这些爱好是以字符串的形式保存在一个字段中的 为了方便存储和使用,我们针对学生的爱好这种数据个数不固定的场景,可以使用数组的形式来存储 测试数据是这样的 
来建一张表,指定了一个array数组类型的字段叫favors,数组中存储字符串,数组中的元素怎么分割呢?通过collection items terminated by ','指定的
 向表中加载数据 
查询数组中的某一个元素,使用arrayName[index] 
角标是从0开始的,如果获取到了不存在的角标则返回null 这就是Array类型的使用了
Map
另外一种常见的集合——map,我们知道map集合里面存储的是键值对,每一个键值对属于Map集合的一个item, 举个例子,有两个学生zhangsan、lisi,每个学生有语文、数学、英语,成绩如下:

针对学生的成绩信息最好也是存储到一个字段中,方便管理和使用,发现学生的成绩都是key-value类型的,所以非常适合使用map类型
建表语句如下:指定scores字段类型为map格式 通过collection items terminated by ','指定了map中元素之间的分隔符 通过map keys terminated by ':'指定了key和value之间的分隔符 
向表中插入数据 
查看表中的数据 
查询所有学生的语文和数学成绩 
在这注意一下,取数据是根据元素中的key获取的,和map结构中元素的位置没有关系
Struct
最后一种复合类型struct,有点像java中的对象,举个例子说明一下, 某学校有2个实习生,zhangsan、lisi,每个实习生都有地址信息,一个是户籍地所在的城市,一个是公司所在的城市, 我们来组织一下数据 
针对这里面的地址信息,不能懒省事使用字符串,否则后期想要获取他们对应的户籍地城市或者公司所在的城市信息时就比较麻烦了 所以在这我们可以考虑使用Struct类型 建表语句如下: 
加载数据 
查询数据 
在这里会发现其实这个需求,使用Array也是可以搞定的吧,只不过是在查询的时候只能通过角标访问,不太方便而已。
Struct和Map的区别
如果从建表语句上来分析,其实这个Struct和Map还是有一些相似之处的 来总结一下:
map中可以随意增加k-v对的个数 struct中的k-v个数是固定的
map在建表语句中需要指定k-v的类型 struct在建表语句中需要指定好所有的属性名称和类型
map中通过[]取值 struct中通过.取值,类似java中的对象属性引用
map的源数据中需要带有k-v struct的源数据中只需要有v即可
总体而言还是map比较灵活,但是会额外占用磁盘空间,因为他比struct多存储了数据的key struct只需要存储value,比较节省空间,但是灵活性有限,后期无法动态增加k-v
案例:符合数据类型的使用

根据这份数据建表,这里面这几个字段分别是int类型、string类型,array类型,map类型,struct类型 不一定非要使用这些复合类型,主要是需要根据具体业务分析,使用复合数据类型可以更方便的操作数据

加载数据 
查询数据 
思考题
问:在mysql中有一张表student(id,name),还有一张表address(stu_id,home,school),还有联系方式表contact(stu_id,mine,parents,others)。如果把这三张表迁移到hive中,如何迁移?
可以一一对应迁移,优点是迁移成本非常低,包括DDL和业务逻辑,几乎不需要修改,可以直接使用。缺点是产生大量的表连接,造成查询慢。可以一对多,mysql中的多张关联表可以创建为hive中的一张表。优点是减少表连接操作。缺点是迁移成本高,需要修改原有的业务逻辑。
实际上,在日常的开发过程中遇到这样的问题,要想比较完美、顺利的解决,一般都分为两个阶段,第一个阶段,现在快捷迁移,就是上面说的一一对应,让我们的系统能跑起来,在此基础之上呢,再做一张大表,尽量包含以上所有字段,例如: stu(id, name, address struct<home,school>, contact struct<…>); 等第二个阶段完工之后了,就可以跑在新的系统里面了。
Hive中的表类型
在Mysql中没有表类型这个概念,因为它就只有一种表。 但是Hive中是有多种表类型的,我们可以分为四种,内部表、外部表、分区表、桶表
内部表
首先看内部表 内部表也可以称为受控表 它是Hive中的默认表类型,表数据默认存储在 warehouse 目录中 在加载数据的过程中,实际数据会被移动到warehouse目录中,就是咱们前面在使用load加载数据的时候,数据就会被加载到warehouse中表对应的目录中 当我们删除表时,表中的数据和元数据将会被同时删除 实际上,我们前面创建的表都属于受控表,前面我们已经演示了,创建一张表,其对应就,在metastore中存储表的元数据信息,当我们一旦从hive中删除一张表之后,表中的数据会被删除,在metastore中存储的元数据信息也会被删除。 这就是内部表的特性。
外部表
建表语句中包含 External 的表叫外部表 外部表在加载数据的时候,实际数据并不会移动到warehouse目录中,只是与外部数据建立一个链接(映射关系) 表的定义和数据的生命周期互相不约束,数据只是表对hdfs上的某一个目录的引用而已,当删除表定义的时候,数据依然是存在的。仅删除表和数据之间引用关系,所以这种表是比较安全的,就算是我们误删表了,数据还是没丢的 我们来创建一张外部表,看一下外部表的建表语句该如何来写 看一下官方文档   
 
官网中的案例如下: 主要就是在建表语句中增加了EXTERNAL 以及在最后通过locatin指定了这个表数据的存储位置,注意这个路径是hdfs的路径 
那根据这个格式我们自己来创建一个外部表 
表创建完以后到hdfs上查询,如果指定的目录不存在会自动创建 
此时到hdfs的/user/hive/warehouse/目录下查看,是看不到这个表的目录的,因为这个表的目录是我们刚才通过location指定的目录 
我们再来看一下metastore中的tbls表,这里看到external_table的类型是外部表。 
下面我们往这个外部表中加载数据,原始数据文件为external_table.data 
加载数据 
此时加载的数据会存储到hdfs的/data/external目录下 
然后查询数据 
接下来尝试删除这个表,看看会发生什么现象 
到hdfs上查看数据,发现之前上传上去的数据还在 
在hive中查询目前所有的表信息,发现external_table表确实被删除了 
然后到metastore中查看,发现metastore中的相应的记录也一并被删除掉了 
这个其实就是前面我们所的外部表的特性,外部表被删除时,只会删除表的元数据,表中的数据不会被删除。 注意:实际上内外部表是可以互相转化的,需要我们做一下简单的设置即可,里面的EXTERNAL参数必须是大写才能生效。

分区表
接下来看一个特殊的表,分区表,这种表的使用场景也是很多的 假设我们的web服务器每天都产生一个日志数据文件,Flume把数据采集到HDFS中,每一天的数据存储到一个日期目录中。我们如果想查询某一天的数据的话,hive执行的时候默认会对所有文件都扫描一遍,然后再过滤出来我们想要查询的那一天的数据 如果你已经采集了一年的数据,这样每次计算都需要把一年的数据取出来,再过滤出来某一天的数据,效率就太低了,会非常浪费资源, 所以我们可以让hive在查询的时候,根据你要查询的日期,直接定位到对应的日期目录。这样就可以直接查询满足条件的数据了,效率提升可不止一点点啊,是质的提升。 想要实现这个功能,就需要使用分区表了 分区可以理解为分类,通过分区把不同类型的数据放到不同目录中 分区的标准就是指定分区字段,分区字段可以有一个或多个,根据咱们刚才举的例子,分区字段就是日期 分区表的意义在于优化查询,查询时尽量利用分区字段,如果不使用分区字段,就会全表扫描,最典型的一个场景就是把天作为分区字段,查询的时候指定天 按照上面的分析,我们来创建一个分区表,使用partitioned by指定区分字段,分区字段的名称为dt,类型为string 
查看表的信息,可以看到分区信息 
数据格式是这样的 
向分区表中加载数据【注意,在这里添加数据的同时需要指定分区信息】 
来查看一下hdfs中的信息,刚才创建的分区信息在hdfs中的体现是一个目录。 由于这个分区表属于内部表, 所以目录还在warehouse这个目录中

也可以手动在表中只创建分区: 
此时会发现hdfs中又多了一个目录,只不过这个分区目录中是没有数据的

向这个分区中添加数据,可以使用刚才的load命令或者hdfs的put命令都可以 
查看hdfs

如何查看我的表目前有哪些分区呢,语法为:
show partitions tblName

刚才增加了一个分区,那我能删除一个分区吗? 必须是可以的! 
到hdfs上确认一下 
注意了,此时分区删除之后,分区中对应的数据也就没有了,因为是内部表,所以分区的数据是会被删掉的 刚才,我们创建了一个分区,如果需要创建多个分区,可以吗? 当然是可以的! 这里再举一个例子。某学校,有若干二级学院,每年都招很多学生,学校的统计需求大部分会根据年份和学院名称作为条件 所以为了提高后期的统计效率,我们最好是使用年份和学院名称作为分区字段 
查看表信息 
数据文件内容 
加载数据 注意:数据文件中只需要有id和name这两个字段的值就可以了,具体year和school这两个分区字段是在加载分区的时候指定的。

查看分区信息 
查看hdfs中的目录 
 
前面我们讲了如何创建、增加和删除分区 还有一个比较重要的是我们该如何查询分区中的数据呢?其实非常简单,分区相当于我们的一个查询条件,直接跟在where后面就可以了。 select * from partition_2; 【全表扫描,没有用到分区的特性】 select * from partition_2 where year = 2019;【用到了一个分区字段进行过滤】 select * from partition_2 where year = 2019 and school = ‘xk’;【用到了两个分区字段进行过滤】  
这就是分区表的主要操作 在这使用的分区表可以认为是内部分区表,内部分区表的应用场景也不多,外部分区表的应用场景才多,外部分区表就是在外部表的基础上又增加了分区。
外部分区表
先来创建一个外部分区表 
其它的操作和前面操作普通分区表是一样的
添加分区数据 
删除分区 
分区删除之后,在这里就看不到分区信息了,那hdfs上的分区目录被删除了吗?
注意:此时分区目录的数据还是在的,因为这个是外部表,所以删除分区也只是删除分区的定义,分区中的数据还是在的,这个和内部分区表就不一样了。

虽然这个分区目录还在,但是刚才我们通过,show partitions 已经查不到分区信息了,所以查询表数据是查不出来的,虽然这个目录确实在这个表对应的hdfs目录中,但是由于这个是一个分区表,这份数据没有和任何分区绑定,所以就查询不出来 这个一定要注意,最容易遇到的一个问题就是,针对分区表,通过hdfs的put命令把数据上传上去了,但是却查不到数据,就是因为没有在表中添加分区信息。 
如果数据已经上传上去了,如何给他们绑定关系呢?就是使用前面咱们讲的alter add partition命令,注意在这里需要通过location指定分区目录 
此时再查询分区数据和表数据,就正常了. 
总结一下: 
load data…partition这条命令做了两件事情,上传数据,添加分区(绑定数据和分区之间的关系) 
上面这三条命令做了两个事情,上传数据,添加分区(绑定数据和分区之间的关系)
桶表
桶表是对数据进行哈希取值,然后放到不同文件中存储 物理上,每个桶就是表(或分区)里的一个文件
什么时候会用到桶表呢? 举个例子,针对中国的人口,主要集中河南、江苏、山东、广东、四川,其他省份就少的多了,你像西藏就三四百万,海南也挺少的,如果使用分区表,我们把省份作为分区字段,数据会集中在某几个分区,其他分区数据就不会很多,那这样对数据存储以及查询不太友好,在计算的时候会出现数据倾斜的问题,计算效率也不高,我们应该相对均匀的存放数据,从源头上解决,这个时候我们就可以采用分桶的概念,也就是使用桶表
下面来建立一个桶表: 这个表的意思是按照id进行分桶,分成4个桶 
这个时候往桶中加载数据的时候,就不能使用load data的方式了,而是需要使用其它表中的数据,那么给桶表加载数据的写法就有新的变化了。 类似这样的写法 insert into table … select … from …;
注意,在插入数据之前需要先设置开启桶操作,不然数据无法分到不同的桶里面
其实这里的分桶就是设置reduce任务的数量,因为你分了多少个桶,最终结果就会产生多少个文件,最终结果中文件的数量就和reduce任务的数量是挂钩的 设置完set hive.enforce.bucketing = true可以自动控制reduce的数量从而适配bucket的个数
hive (default)> set hive.enforce.bucketing=true
初始化一个表,用于向桶表中加载数据 原始数据文件是这样的 

向桶表中加载数据  查看桶表中的数据  按照我们设置的桶的数量为4,这样在hdfs中会存在4个对应的文件,每个文件的大小是相似的  到hdfs上查看桶表中的文件内容,可以看出是通过对buckets取模确定的  这样就实现了数据分桶存储。 桶表的主要作用:
1 数据抽样 假如我们使用的是一个大规模的数据集,我们只想去抽取部分数据进行查看.使用bucket表可以变得更加的高效 select * from bucket_tb tablesample(bucket 1 out of 4 on id); tablesample是抽样语句 语法解析:TABLESAMPLE(BUCKET x OUT OF y ON column) y尽可能是桶表的bucket数的倍数或者因子,而且y必须要大于等于x y表示是把桶表中的数据随机分为多少桶 x表示取出第几桶的数据 例如: bucket 1 out of 4 on id:根据id对桶表中的数据重新分桶,分成4桶,取出第1桶的数据 bucket 2 out of 4 on id:根据id对桶表中的数据重新分桶,分成4桶,取出第2桶的数据 bucket 3 out of 4 on id:根据id对桶表中的数据重新分桶,分成4桶,取出第3桶的数据 bucket 4 out of 4 on id:根据id对桶表中的数据重新分桶,分成4桶,取出第4桶的数据 验证一下效果,这里面四个SQL语句,每个SQL语句取出一个桶的数据,最终的总和就是表中的所有数据
  2. 提高某些查询效率 例如:join查询,可以避免产生笛卡尔积的操作 select a.id,a.name,b.addr from a join b on a.id = b.id; 如果a表和b表已经是分桶表,而且分桶的字段是id字段,那么做这个操作的时候就不需要再进行全表笛卡尔积了,因为分桶之后相同规则的id已经在相同的文件里面了,这样a表的每个桶就可以和b表的每个桶直接join,而不用全表join了。
视图
Hive中,也有视图的概念,那我们都知道视图实际上是一张虚拟的表,是对数据的逻辑表示, 它的主要作用是为了降低查询的复杂度。 那我们在Hive中如何来创建一个视图呢? 需要使用create view命令, 下面我们来创建一个视图  此时通过show tables也可以查看到这个视图
 查看视图的结构,显示的内容和表显示的内容是没有区别的 
通过视图查询数据 
注意:视图在/user/hive/warehouse中是不存在的。因为它只是一个虚拟的表。
元数据在metahouse中的体现 
|