本节继续讨论HiveQL,Hive查询语言,如何向Hive表中添加数据,操纵数据和从表中将数据提取到文件系统。
之前已经学习过Hive表的创建,本节主要关注向表中填充数据,使表中有数据可供查询。
先行创建本节要使用的表employees:
CREATE TABLE employees (
name STRING,
salary FLOAT,
subordinates ARRAY<STRING> COMMENT '下属',
deductions MAP<STRING,FLOAT> COMMENT '扣费',
address STRUT<street:STRING,city:STRING,state:STRING,zip:INT>
)
PARTITIONED BY(country STRING,state STRING);
向管理表(非外部表)中加载数据
由于Hive没有行级的insert,update和delete操纵,向表中加载数据的唯一方法就是“批量”加载数据。如下示例,想一个分区表中加载一批数据:
LOAD DATA LOCAL INPATH '${env:HOME}/calafornia-employees'
OVERWRITE INTO TABLE employees
PARTITION (country = 'US', state = 'CA');
在加载数据之前首先要要保证已经创建有符合条件的分区。在这个示例中,数据被加载后再Hive中的目录为:
hdfs://master_server/user/hive/warehouse/mydb.db/employees/country=US/state=CA
这个示例需要注意几点:
关键字OVERWRITE:加上该词说明如果之前已经箱盖分区中加载过数据的话,则之前的数据会首先被“清洗掉”,然后才加载新数据;如果没有加
关键字OVERWRITE的话,如:
LOAD DATA LOCAL INPATH '${env:HOME}/calafornia-employees'
INTO TABLE employees
PARTITION (country = 'US', state = 'CA');
就不会将已有的数据清洗,而是直接在原有数据后边追加新的数据。
关键字LOCAL:表明是从本地文件系统的文件夹中加载数据,如果不加
LOCAL关键字,则表明是从HDFS系统的文件夹中加载数据:
LOAD DATA INPATH '${env:HOME}/calafornia-employees'
INTO TABLE employees
PARTITION (country = 'US', state = 'CA');
另外一点就是,对于要指定要加载的文件的路径,指定一个文件夹是比较符合常规的,这要比每次单独指定文件夹中的一个具体的文件要好,这样Hive会一次把指定文件夹下的所有的位安全都拷贝到Hive仓库中。
Hive在加载数据时并不会管要加载的数据是否和表定义模式相匹配,但是会验证文件格式,比如说,在表中第一的保存的数据文件为SEQUENCEFILE,那么加载后的问价就必须为SEQUENCEFILE文件。
通过HiveQL查询语句添加数据
INSERT语句可以让用户通过一个HiveQL Query语句向Hive表中插入数据。看下面的一个示例(这里假设已经定义过表staged_employees):
INSERT OVERWRITE TABLE employess
PARTTITION (country = 'US',state = 'OR')
SELECT * FROM staged_employees se
WHERE se.cnty = 'US' AND se.st = 'OR';
OVERWRITE关键字,我们已经讨论过。但对于本示例来说,如果去掉
OVERWRITE或用
INTO替换掉,如:
INSERT INTO TABLE employess
PARTTITION (country = 'US',state = 'OR')
SELECT * FROM staged_employees se
WHERE se.cnty = 'US' AND se.st = 'OR';
那么Hive就会将数据以“追加”的方式插入数据到employess表。
这个示例在一个场景中特别有用:数据已经被保存在另外一个表中,如Hive的外部表,然后用户想把某些数据做最终的保存到分区表中;或者从原有的数据源表中将数据保存成符合用的要求的不同的数据记录的格式。
然而,如果staged_employees中数据特别大,有可能你需要执行很多次这样的插入查询导入数据的话,比如说美国部分的数据就要执行65次之多。Hive提供了另外一种
INSERT语法,是你只扫描一次表就可以将数据插入到对应的分区中:
ROM staged_employees se
INSERT INTO TABLE employess
PARTTITION (country = 'US',state = 'OR')
SELECT * WHERE se.cnty = 'US' AND se.st = 'OR';
INSERT INTO TABLE employess
PARTTITION (country = 'US',state = 'CA')
SELECT * WHERE se.cnty = 'US' AND se.st = 'CA';
INSERT INTO TABLE employess
PARTTITION (country = 'US',state = 'IL')
SELECT * WHERE se.cnty = 'US' AND se.st = 'IL';
...
这种
INSERT语法也可以一次插入多个表。
动态分区添加数据
就上例来说,好友一个问题,那就是如果有特别多的分区要创建的话,用户不得不要写许多HiveQL语句!幸运的是,Hive提供一个叫动态分区的功能,可以基于用户的查询参数推断要创建的分区。通过与动态分区相比较,之前我们讨论的分区插入数据被称为静态分区数据导入。
将上面的静态分区作如下修改,就变成动态分区数据导入的示例:
INSERT OVERWRITE TABLE employees
PARTITION (country,state)
SELECT ...,se.cnty,se.st
FROM staged_employees se
对于本例来说,Hive会通过SELECT语句中最后两列se.cnty和se.st的值来决定employees表中的分区的key,contry和state。Hive在动态分区插入操作中特别强调源表的数据列的值和要插入分区的key的值的位置关系,而不是通过名字匹配,这也是为什么在staged_employees表中给国家和州定义不同的名字的原因。
假设staged_employees有100个国家和州的匹配对儿,那么执行玩这个HiveQL语句后,employees就会有100个国家分区!
用户也可以混合使用动态分区和静态分区数据插入,如下例中,我们是用了一个静态的country(US)和动态的州的值:
INSERT OVERWRITE TABLE employees
PARTITION (country = 'US',state)
SELECT ...,se.cnty,se.st
FROM staged_employees se
WHERE se.cnty = 'US';
要注意的是,必须要把静态分区的key放在动态分区的key之前。
默认请情况下,动态分区插入的功能是被禁用的,当被激活后,Hive默认会工作在“严格(strict)”模式下。在“严格(strict)”模式下,必须使用静态分区和动态分区混合使用的方式,这主要是避免一些不好的数据查询设计。
要使用动态模式,用户首先要激活动态分区的一些参数设置:
hive> set hibe.exec.dynamic.partition=true;
hive> set hibe.exec.dynamic.mode=nonstrict;
hive> set hibe.exec.max.dynamic.partitions.pernode=1000;
hive> INSERT OVERWRITE TABLE employees PARTITION(country,state)
> SELECT ...,se.cnty,se.st FROM staged_employees se;
nonstrict可以完全由用户的查询参数来动态创建所有的分区。
在一个HiveQL中创建表和加载数据
在Hive中,用户可以完全使用一条语句创建表并同时加载数据:
CREATE TABLE ca_employees
AS SELECT name salary,address
FROM employees
WHERE state = 'CA';
这种方法特别适用于在一个大表中提取一个子数据集的场景,这种功能不适合外部表,因外外部表是在定义时直接给其制定一个数据文件的路径。
导出数据
之前讲的都是如何将数据加载到Hive表中,那么如何将表中数据导出表呢?
用户可以使用INSERT ... DIRECTORY ...语句导出数据,示例如下:
INSERT OVERWRITE LOCAL DIRECTORY 'tmp/ca_employees'
SELECT name,salary,address
FROM employees
WHERE state = 'CA';
其中
OVERWRITE和
LOCAL的意义同上。
和加载数据一样,用户同样可以在一个语句中将数据导出到多个文件夹:
FROM staged_employees se
INSERT OVERWRITE DIRECTORY '/tmp/or_employees'
SELECT * FROM se.cnty = 'US' AND se.st ='OR'
INSERT OVERWRITE DIRECTORY '/tmp/CA_employees'
SELECT * FROM se.cnty = 'US' AND se.st ='CA'
INSERT OVERWRITE DIRECTORY '/tmp/IL_employees'
SELECT * FROM se.cnty = 'US' AND se.st ='IL'