前面介绍了 Hive 中的 DDL 语言,接下来我们将具体看看 HIve 中对数据的具体操作。本篇将主要探讨如何使用 LOAD, INSERT, IMPORT 和 EXPORT关键词来进行数据转换操作。
1. 数据转换 – LOAD
在 Hive 中可以通过 LOAD
关键词来移动数据。数据移动代表将数据从原有路径移到目标路径中,相当于剪切操作。以下展示了如果从本地或者 HDFS 中将数据移到 Hive 表(内部表/管理表)或者分区表中。
LOCAL
关键词用来指定文件在主机中的位置,如果 没有被指定,文件将会从INPATH
后指定的 完整URL [full Uniform Resource Identifier ] 或者 从Hive 配置属性fs.default.name进行加载。INPATH
后面可以跟相对路径或绝对路径。路径指向的文件或者文件夹(文件夹中所有文件)都会被加载。在path 中不允许指定子目录。
如果要加载数据到分区表,分区字段需要被指定。关键词 OVERWRITE
用来决定是添加还是替换目标表/分区表中的数据。
以下是将文件加载到 Hive 表的示例:
数据加载方式 |
具体实现方法
|
Local -> Hive table
|
将本地数据加载到 Hive 表中:
jdbc:hive2://> LOAD DATA LOCAL INPATH |
Local ->Hive分区表
|
将本地数据加载到 Hive 分区表中[Hive partition table] :
jdbc:hive2://> LOAD DATA LOCALINPATH |
使用默认系统路径
|
使用系统默认路径将 HDFS 数据加载到 Hive 表中:
jdbc:hive2://> LOAD DATA INPATH |
使用完整的 URL
|
使用完整的 URL [ full URI ] 将数据从 HDFS 加载到 Hive 表中:
jdbc:hive2://> LOAD DATA INPATH |
2. 数据转换 – INSERT
我们可以使用 INSERT
关键词来从 Hive 表/分区表中提取数据。类似于关系数据库,Hive 支持从另一张表进行插入,这是我们为一张表填充已有数据的常用方式。 Hive 的 INSERT
语句有一些类似于关系数据库 INSERT
的语法。而且,Hive可以通过支持 OVERWRITE,multiple INSERT
dynamic partition INSERT
, 将数据插入到文件中来提升了 insert 语句的功能。让我们看一下例子:
- 以下是标准的利用
SELECT
语句进行插入:
--检查目标表是否为空.
jdbc:hive2://> SELECT name, work_place, sex_age
. . . . . . .> FROM employee;
+-------------+-------------------+----------------+
|employee.name|employee.work_place|employee.sex_age|
+-------------+-------------------+----------------+
+-------------+-------------------+----------------+
No rows selected (0.115 seconds)
--利用 Select 出来的数据填充到 employee 表中
jdbc:hive2://> INSERT INTO TABLE employee
. . . . . . .> SELECT * FROM ctas_employee;
No rows affected (31.701 seconds) -- 执行时间太长,基本花在数据的读取,磁盘的I/O
--验证数据的加载
jdbc:hive2://> SELECT name, work_place, sex_age FROM employee;
+-------------+----------------------+-------------------------+
|employee.name| employee.work_place | employee.sex_age |
+-------------+----------------------+-------------------------+
| Michael |["Montreal","Toronto"]|{"sex":"Male","age":30} |
| Will |["Montreal"] |{"sex":"Male","age":35} |
| Shelley |["New York"] |{"sex":"Female","age":27}|
| Lucy |["Vancouver"] |{"sex":"Female","age":57}|
+-------------+----------------------+-------------------------+4 rows selected (0.12 seconds)
- 使用公用表表达式 [ CTE - Common Table Expression ] 插入数据:
jdbc:hive2://> WITH a AS (SELECT * FROM ctas_employee )
. . . . . . .> FROM a
. . . . . . .> INSERT OVERWRITE TABLE employee
. . . . . . .> SELECT *;No rows affected (30.1 seconds)
- 只Scanning 源表一次来执行多次插入:
jdbc:hive2://> FROM ctas_employee
. . . . . . .> INSERT OVERWRITE TABLE employee
. . . . . . .> SELECT *
. . . . . . .> INSERT OVERWRITE TABLE employee_internal
. . . . . . .> SELECT * ;No rows affected (27.919 seconds)
注意:INSERT OVERWRITE
语句 会替换目标表中的数据;INSERT INTO
会将数据附加到表/分区表中。
当向分区表插入数据的时候,我们需要指定分区字段。而不是指定静态分区的静态值。Hive还支持为分区表动态赋值。当数据量很大的时候使用动态分区很有用,此时我们是不知道具体的分区值。例如,日期被动态的用作分区字段。
动态分区默认情况是 Disable 的,我们需要通过设置以下属性来让它正常工作:
jdbc:hive2://> SET hive.exec.dynamic.partition=true;No rows affected (0.002 seconds)
By default, the user must specify at least one static partition column. This is to avoid accidentally overwriting partitions. To disable this restriction, we can set the partition mode to nonstrict
from the default strict mode before inserting into dynamic partitions as follows:
默认情况下,用户必须至少指定一个静态分区字段。主要是用来避免分区表覆盖失误。在插入到动态分区之前,你可以通过默认strict 模式中设置分区模式为 nonstrict
来禁止该约束。具体代码如下所示:
jdbc:hive2://> SET hive.exec.dynamic.partition.mode= nonstrict ;
No rows affected (0.002 seconds)
jdbc:hive2://> INSERT INTO TABLE employee_partitioned
. . . . . . .> PARTITION(year, month)
. . . . . . .> SELECT name, array('Toronto') as work_place,
. . . . . . .> named_struct("sex","Male","age",30) as sex_age,
. . . . . . .> map("Python",90) as skills_score,
. . . . . . .> map("R&D",array('Developer')) as depart_title,
. . . . . . .> year(start_date) as year, month(start_date) as month
. . . . . . .> FROM employee_hr eh
. . . . . . .> WHERE eh.employee_id = 102;No rows affected (29.024 seconds)
注意:复合类型的构造函数通常用来将一个恒定值赋值给一个复合数据类型的列。
SELECT
语句中提取数据并加载到本地或 HDFS 文件。但是,它目前只支持 OVERWRITE
关键词,不支持 INTO。这意味着我们不能将数据叠加到已有的文件中。默认情况下,列是通过^A 进行分割的,行是通过换行符进行分割。从 Hive 0.11.0 版本开始,可以指定行分隔符。下面我们将看一些将数据插入到文件的例子:
我们可以使用默认的行分隔符来将数据插入到文件中。在 Hadoop 的一些版本中,本地目录路径只支持2级以下的目录级别。我们可以通过 配置 hive.insert.into.multilevel.dirs=true 参数来解决这个问题。
jdbc:hive2://> INSERT OVERWRITE LOCAL DIRECTORY '/tmp/output1' . . . . . . .> SELECT * FROM employee;No rows affected (30.859 seconds)
注意:默认情况下,执行 Insert 操作的时候,大部分的文件是通过reducer 创建的。你可以通过以下的 HDFS 命令来将这些文件整合到一起:
hdfs dfs –getmerge hdfs://<host_name>:8020/user/dayongd/output /tmp/test
- 利用特定的 行分隔符 将数据插入到本地文件中 :
jdbc:hive2://> INSERT OVERWRITE LOCAL DIRECTORY '/tmp/output2'. . . . . . .> ROW FORMAT DELIMITED FIELDS TERMINATED BY ','. . . . . . .> SELECT * FROM employee;No rows affected (31.937 seconds)
--验证分隔符:vi /tmp/output2/000000_0Michael,Montreal^BToronto,Male^B30,DB^C80,Product^CDeveloper^DLeadWill,Montreal,Male^B35,Perl^C85,Product^CLead^BTest^CLeadShelley,New York,Female^B27,Python^C80,Test^CLead^BCOE^CArchitectLucy,Vancouver,Female^B57,Sales^C89^BHR^C94,Sales^CLead
- 利用
SELECT
语句将一张表的数据 INSERT 到多个文件中:
jdbc:hive2://> FROM employee. . . . . . .> INSERT OVERWRITE DIRECTORY '/user/dayongd/output'. . . . . . .> SELECT *. . . . . . .> INSERT OVERWRITE DIRECTORY '/user/dayongd/output1'. . . . . . .> SELECT * ;No rows affected (25.4 seconds)
注意:除了使用 Hive 的 Insert 语句, Hive可以通过 HDFS 命令来提取数据到本地或远程,它支持 append 和 overwrite 两种写入方式。 hive -e 'quoted_hql_string'
或者 hive -f <hql_filename>
命令可以执行一个 Hive 查询语句或者查询文件。Linux 重定向操作符[redirect operators] 和管道可以重定向结果集。一下是一些示例:
- 键查询结果追加到本地文件:
$ hive -e 'select * from employee' >> test
- 覆盖本地文件:
$ hive -e 'select * from employee' > test
- 追加到 HDFS 文件:
$ hive -e 'select * from employee'|hdfs dfs -appendToFile - /user/dayongd/output2/test
- 覆盖 HDFS 文件:
$ hive -e 'select * from employee'|hdfs dfs -put -f - /user/dayongd/output2/test
3. 数据转换 – EXPORT 和 IMPORT
当我们使用 Hive 的时候,有时候需要将数据转移到不同的环境中,或者我们需要备份一部分数据。从 Hive 0.8.0 开始便支持 EXPORT
和 IMPORT
语句来对数据迁移/备份到 HDFS 中。
EXPORT 语句将会从表或分区表中导出数据和元数据。导出的元数据文件被称为 _metadata。导出数据的子目录被称为 data:
jdbc:hive2://> EXPORT TABLE employee TO '/user/dayongd/output3';
No rows affected (0.19 seconds)
After EXPORT
, we can manually copy the exported files to other Hive instances or use Hadoop distcp
commands to copy to other HDFS clusters. Then, we can import the data in the following manner:
在导出后,我们可以手动复制导出的文件到其他的 Hive 实例中,或者使用 Hadoop distcp 命令将文件拷贝到其他的 Hadoop 集群。目前我们有以下方法导入数据:
- 将数据从文件导入到一张表中,表名跟文件名相同。如果表存在则会报错:
jdbc:hive2://> IMPORT FROM '/user/dayongd/output3';Error: Error while compiling statement: FAILED: SemanticException [Error 10119]: Table exists and contains data files (state=42000,code=10119)
- 导入到一张新表中:
jdbc:hive2://> IMPORT TABLE empolyee_imported FROM. . . . . . .> '/user/dayongd/output3';No rows affected (0.788 seconds)
- 导入到一张外部表中,在这里
LOCATION
属性是可选的:
jdbc:hive2://> IMPORT EXTERNAL TABLE empolyee_imported_external. . . . . . .> FROM '/user/dayongd/output3'. . . . . . .> LOCATION '/user/dayongd/output4' ;No rows affected (0.256 seconds)
- 对分区表进行导出&导入:
jdbc:hive2://> EXPORT TABLE employee_partitioned partition. . . . . . .> (year=2014, month=11) TO '/user/dayongd/output5';No rows affected (0.247 seconds)
jdbc:hive2://> IMPORT TABLE employee_partitioned_imported. . . . . . .> FROM '/user/dayongd/output5';No rows affected (0.14 seconds)
扩展阅读:
有兴趣的同学可以继续参考如下链接的该篇博客,博主翻译了《Programing Hive》中的数据操作 DML: http://flyingdutchman.iteye.com/blog/1868600