HiveQL DML—数据加载和插入

使用LOAD DATA加载数据

Hive使用LOAD DATA加载数据的时候,不会进行任何的数据转换操作。完全就是简单的将数据文件复制/移动到表对应的路径下

语法

LOAD DATA [LOCAL] INPATH 'filepath' [OVERWRITE] INTO TABLE tablename [PARTITION (partcol1=val1, partcol2=val2 ...)]
LOAD DATA [LOCAL] INPATH 'filepath' [OVERWRITE] INTO TABLE tablename [PARTITION (partcol1=val1, partcol2=val2 ...)] [INPUTFORMAT 'inputformat' SERDE 'serde'] (3.0 or later)

说明
Hive 3.0之前,加载操作就是简单的将数据文件复制/移动到表对应的路径下

  • filepath可以是:
    • 相对路径:如project/data1
    • 绝对路径:如/user/hive/project/data1
    • 一个完整的带scheme和(可选)授权信息的URI,如hdfs://namenode:9000/user/hive/project/data1
  • 加载目标可以是一个表或一个分区。如果分区表,则必须指定所有分区字段的值来确定加载的分区。
  • filepath可以是一个文件(这种情况下Hive将文件移动到表目录下)也可以是一个目录(在这种情况下Hive将该目录中的所有文件移动到表目录下)。这两种情况下,filepath都会处理一组文件。
  • 如果指定了关键字LOCAL,则:
    • LOAD命令将在本地文件系统查找filepath。如果指定的是相对路径,则相对于用户当前的工作目录。用户可以为本地文件指定一个完整的URI,例如:file:///user/hive/project/data1
    • LOAD命令复制由filepath指定的所有文件到目标文件系统。通过查看表的Location属性推断目标文件系统。然后将复制的数据文件移到表中。
    • 注意:如果是在一个HiveServer2实例上运行Load命令,则其本地路径指的是在HiveServer2实例上的路径。同时,HiveServer2必须具有访问该文件的适当权限。
  • 如果没指定关键字LOCAL,HIVE要么使用完整URI的文件路径(如果指定),要么应用以下规则:
    • 如果未指定scheme或授权信息,Hive将使用来自Hadoop配置变量fs.default.name指定的Namenode URI的scheme和授权信息。
    • 如果不是绝对路径,那么HIVE会相对于 /user/<username>来解释路径。
    • HIVE将移动filepath所指定的文件到表(或分区)的文件路径。
  • 如果使用了OVERWRITE关键字,则目标表(或分区)的内容将被filepath指定的文件内容覆盖。

从Hive 3.0开始,支持另一种方式的Load操作,它会在Hive内部重写为一个INSERT AS SELECT操作。

  • 如果表有分区,但Load命令没有指定分区,则Load将被转换成INSERT AS SELECT,并且假设最后一组字段是分区字段。如果文件不符合预期的Schema,则它会抛出一个错误。
  • 如果是分桶表,则遵循以下规则:
    • strict 模式:启动一个INSERT AS SELECT作业。
    • non-strict模式:如果文件名符合命名惯例(如果该文件属于桶0,它应该被命名为000000_0或000000_0_copy_1,或者如果它属于桶2名应该像000002_0或000002_0_copy_3 等。 ),那么这将是一个纯粹的复制/移动操作,反之,它将启动一个INSERT AS SELECT工作。
  • filepath可以包含子目录,提供的每个文件都符合该模式。
  • inputformat可以是Hive的任何输入格式,如text,ORC等
  • serde可以关联Hive SERDE
  • inputformatserde都是大小写敏感的。

注意

  • Hive3.0之前,filepath不能包含子目录
  • 如果不指定关键字LOCAL,则filepath引用的文件必须同Hive表(或分区)的位置处于同一文件系统中
  • Hive会做一些最基础的检查,以确保这些加载文件匹配目标表。目前,如果该目标表的存储格式为sequencefile,它会检查加载的文件是否也为sequencefiles,以此类推。
  • Gzip、Bzip2压缩格式的文本文件可以直接加载到存储格式为TextFile的表中

示例

示例:加载本地数据

--加载到表
> LOAD DATA LOCAL INPATH
 '/home/dayongd/Downloads/employee_hr.txt'
 OVERWRITE INTO TABLE employee_hr;

--加载到分区
> LOAD DATA LOCAL INPATH
'/home/dayongd/Downloads/employee.txt'
OVERWRITE INTO TABLE employee_partitioned
PARTITION (year=2018, month=12);

示例:加载HDFS数据

> LOAD DATA INPATH
'/tmp/hivedemo/data/employee.txt'
INTO TABLE employee;

--使用完整的URI
> LOAD DATA INPATH
> 'hdfs://ns001/tmp/employee.txt'
> OVERWRITE INTO TABLE employee;

将查询结果插入到表中

使用insert语句可以将查询结果插入到表中

语法

--标准语法
INSERT OVERWRITE TABLE tablename1 [PARTITION (partcol1=val1, partcol2=val2 ...) [IF NOT EXISTS]] select_statement1 FROM from_statement;
INSERT INTO TABLE tablename1 [PARTITION (partcol1=val1, partcol2=val2 ...)] select_statement1 FROM from_statement;
 
--Hive扩展(多个 inserts)
FROM from_statement
INSERT OVERWRITE TABLE tablename1 [PARTITION (partcol1=val1, partcol2=val2 ...) [IF NOT EXISTS]] select_statement1
[INSERT OVERWRITE TABLE tablename2 [PARTITION ... [IF NOT EXISTS]] select_statement2]
[INSERT INTO TABLE tablename2 [PARTITION ...] select_statement2] ...;

FROM from_statement
INSERT INTO TABLE tablename1 [PARTITION (partcol1=val1, partcol2=val2 ...)] select_statement1
[INSERT INTO TABLE tablename2 [PARTITION ...] select_statement2]
[INSERT OVERWRITE TABLE tablename2 [PARTITION ... [IF NOT EXISTS]] select_statement2] ...;
 
--Hive扩展 (动态分区inserts)
INSERT OVERWRITE TABLE tablename PARTITION (partcol1[=val1], partcol2[=val2] ...) select_statement FROM from_statement;
INSERT INTO TABLE tablename PARTITION (partcol1[=val1], partcol2[=val2] ...) select_statement FROM from_statement;

说明

  • INSERT OVERWRITE 会覆盖在表或分区中的已有数据

    • 除非用于分区时提供了 IF NOT EXISTS(从Hive 0.9.0开始)。
    • 从Hive 2.3.0(HIVE-15880)开始,如果表中有TBLPROPERTIES(“auto.purge” =“true”),在表上执行INSERT OVERWRITE查询时,该表以前的数据不被移动到回收站。此功能仅适用于内部表,如果 "auto.purge"属性未设置或设置为false,则该功能会被关闭。
  • INSERT INTO将追加数据到表或分区,原有数据保留不变。

    • 从Hive 0.13.0开始,可以使用TBLPROPERTIES(“Immutable”=“true”)创建表使表不可变。默认是"Immutable"=“false”,此时如果表中已经存在数据,则不允许INSERT INTO插入数据到表中,但如果表中数据为空,则INSERT INTO操作仍然有效。INSERT OVERWRITE行为不受"immutable"表属性的影响。不可变表可以保护多次运行数据加载脚本导致的错误,防止意外更新。对不可变表的第一个插入成功,之后的插入则失败,这样,在表中的只有一组数据,而不是白白保留多个数据副本。
  • 插入的目标可以是表或者分区,如果是分区表,则必须指定所有分区字段的值来确定要insert的分区。如果 hive.typecheck.on.insert 的值设置为true,这些字段值会进行验证,转换并正常化,以符合他们的字段类型(Hive 0.12.0以后)。

  • 可以在同一个查询中指定多个INSERT子句(也称为多表插入)。

  • 每个select语句的的输出被写入到选中的表(或分区)。目前,OVERWRITE关键字是强制性的,意味着所选择的表或分区的内容将被对应的select语句的输出覆盖。

  • 输出格式和序列化类是由表的元数据来确定

  • 从Hive 0.14开始,如果一个表具有一个实现了AcidOutputFormat的OUTPUTFORMAT,并且Hive系统配置了用一个ACID的实现作为事务管理器,则对该表,INSERT OVERWRITE操作将会被禁用。这是为了避免用户无意间改写事务历史,可以通过使用 TRUNCATE TABLE(对于非分区表)或在DROP PARTITION后使用INSERT INTO实现同样的功能。

  • 从Hive 1.1.0开始,TABLE关键字是可选的。

  • 从Hive 1.2.0开始,每个INSERT INTO T能够提供类似INSERT INTO T(Z,X,C1)的一个字段列表。详见 HIVE-9481的例子。

注意

  • 多表插入可使数据扫描所需的次数最小化。通过对输入数据只扫描一次(并应用不同的查询操作符),Hive可以将数据插入多个表中。
  • 从HIVE 0.13.0开始,Select子句可以包含一个或多个公共表表达式(CTE)。

示例

示例:标准语法

> CREATE TABLE employee_orc(
name string,
work_place array<string>,
gender_age struct<gender:string,age:int>,
skills_score map<string,int>,
depart_title map<string,array<string>>
)
COMMENT 'ORC Format'
STORED AS ORC;
> insert into employee_orc select * from employee_internal;
> select * from employee_orc;
+--------------------+--------------------------+-------------------------------+----------------------------+----------------------------------------+
| employee_orc.name  | employee_orc.work_place  |    employee_orc.gender_age    | employee_orc.skills_score  |       employee_orc.depart_title        |
+--------------------+--------------------------+-------------------------------+----------------------------+----------------------------------------+
| Michael            | ["Montreal","Toronto"]   | {"gender":"Male","age":30}    | {"DB":80}                  | {"Product":["Developer^DLead"]}        |
| Will               | ["Montreal"]             | {"gender":"Male","age":35}    | {"Perl":85}                | {"Product":["Lead"],"Test":["Lead"]}   |
| Shelley            | ["New York"]             | {"gender":"Female","age":27}  | {"Python":80}              | {"Test":["Lead"],"COE":["Architect"]}  |
| Lucy               | ["Vancouver"]            | {"gender":"Female","age":57}  | {"Sales":89,"HR":94}       | {"Sales":["Lead"]}                     |
+--------------------+--------------------------+-------------------------------+----------------------------+----------------------------------------+

示例:插入部分字段

> CREATE TABLE employee_simple( 
name string,
work_place string
);
> INSERT INTO TABLE employee_simple (name)
SELECT name FROM employee_internal WHERE name = 'Will';
> select * from employee_simple;
+-----------------------+-----------------------------+
| employee_simple.name  | employee_simple.work_place  |
+-----------------------+-----------------------------+
| Will                  | NULL                        |
+-----------------------+-----------------------------+

示例:从CET语句插入数据

> CREATE TABLE ctas_employee as SELECT * FROM employee_internal;
> WITH a as (
SELECT * FROM ctas_employee
)
FROM a
INSERT OVERWRITE TABLE employee_orc
SELECT *;
> select * from employee_orc;
+--------------------+--------------------------+-------------------------------+----------------------------+----------------------------------------+
| employee_orc.name  | employee_orc.work_place  |    employee_orc.gender_age    | employee_orc.skills_score  |       employee_orc.depart_title        |
+--------------------+--------------------------+-------------------------------+----------------------------+----------------------------------------+
| Michael            | ["Montreal","Toronto"]   | {"gender":"Male","age":30}    | {"DB":80}                  | {"Product":["Developer^DLead"]}        |
| Will               | ["Montreal"]             | {"gender":"Male","age":35}    | {"Perl":85}                | {"Product":["Lead"],"Test":["Lead"]}   |
| Shelley            | ["New York"]             | {"gender":"Female","age":27}  | {"Python":80}              | {"Test":["Lead"],"COE":["Architect"]}  |
| Lucy               | ["Vancouver"]            | {"gender":"Female","age":57}  | {"Sales":89,"HR":94}       | {"Sales":["Lead"]}                     |
+--------------------+--------------------------+-------------------------------+----------------------------+----------------------------------------+

示例:多表插入

> FROM ctas_employee
INSERT OVERWRITE TABLE employee_orc
SELECT *
INSERT OVERWRITE TABLE employee_internal
SELECT *
INSERT OVERWRITE TABLE employee_partitioned
PARTITION (year=2018, month=9)
SELECT *
;

示例:动态分区插入

--创建表employee_hr并加载数据
> CREATE TABLE IF NOT EXISTS employee_hr (
name string,
employee_id int,
sin_number string,
start_date date
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '|';

> LOAD DATA PATH "/tmp/employee_hr.txt" INTO TABLE employee_hr;

--设置动态分区使用nonstrict模式
> SET hive.exec.dynamic.partition=true;
> SET hive.exec.dynamic.partition.mode=nonstrict;

> show partitions employee_partitioned;
+--------------------+
|     partition      |
+--------------------+
| year=2018/month=9  |
+--------------------+

--示例中,复杂类型构造函数用于创建复杂数据类型的常量值
> INSERT INTO TABLE employee_partitioned
PARTITION(year, month)
SELECT name, 
array('Toronto') as work_place,
named_struct("gender","Male","age",30) as gender_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;
> show partitions employee_partitioned;
+---------------------+
|      partition      |
+---------------------+
| year=2012/month=11  |
| year=2018/month=9   |
+---------------------+

将查询数据写入到文件系统中

INSERT语句除了可以将查询数据插入到表中,还可以将查询数据写入到文件系统中,此时INSERT语句的作用和LOAD正好相反,通常用于将SELECT的结果写入到本地或者HDFS文件系统中。

语法

--标准语法
INSERT OVERWRITE [LOCAL] DIRECTORY directory1
  [ROW FORMAT row_format] [STORED AS file_format]
  SELECT ... FROM ...
 
--Hive 扩展 (多个insert):
FROM from_statement
INSERT OVERWRITE [LOCAL] DIRECTORY directory1 select_statement1
[INSERT OVERWRITE [LOCAL] DIRECTORY directory2 select_statement2] ...

说明

  • 目录可以是一个完整的URI。如果未指定scheme或授权信息,Hive将使用来自Hadoop配置变量fs.default.name指定的HDFS URI的scheme或授权。
  • 如果使用LOCAL关键词,Hive将数据写入到本地文件系统的目录上。
  • 写入文件系统的数据被默认序列化为由^A做字段分割符,换行符做行分隔符的文本。如果表的任何字段都不是原始类型(而是MAP、ARRAY、STRUCT、UNION),则这些列被序列化为JSON格式。

注意

  • INSERT OVERWRITE语句可以在同一个查询中将数据写入到HDFS目录、本地目录以及表(或分区)中。
  • INSERT OVERWRITE语句是Hive提取大量数据到HDFS文件目录的最佳方式。Hive可以从一个map-reduce作业中将数据并行写入HDFS目录。
  • 该语句只支持OVERWRITE,也就是目录是被覆盖的;换句话说,如果指定的路径存在,则该目录将被关闭并被替换为新的输出。
  • 从Hive 0.11.0开始,可以使用指定的分隔符;在早期版本中,它始终是^A字符(\001)。但是,Hive版本0.11.0到1.1.0中,自定义分隔符只支持本地写入,这个bug在Hive 1.2.0中得到了修复(见 HIVE-5672)。
  • 在Hive 0.14中,插入符合ACID的表将在SELECT和INSERT期间禁用矢量化,这将自动完成。插入数据后的ACID表仍然可以使用矢量化来查询。

示例

示例:将查询数据写入到本地目录

INSERT OVERWRITE LOCAL DIRECTORY '/tmp/output1'
SELECT * FROM employee_internal;

示例:将查询数据写入到本地目录同时指定字段分隔符

> INSERT OVERWRITE LOCAL DIRECTORY '/tmp/output2'
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
SELECT * FROM employee_internal;
> !sh cat /tmp/output2/000000_0
Michael,MontrealToronto,Male30,DB80,ProductDeveloper^DLead
Will,Montreal,Male35,Perl85,ProductLeadTestLead
Shelley,New York,Female27,Python80,TestLeadCOEArchitect
Lucy,Vancouver,Female57,Sales89HR94,SalesLead

示例:多INSERT语句

> FROM employee_internal
INSERT OVERWRITE DIRECTORY '/user/output3'
SELECT *
INSERT OVERWRITE DIRECTORY '/user/output4'
SELECT name ;

> !sh hadoop fs -cat /user/output3/000000_0
MichaelMontrealTorontoMale30DB80ProductDeveloper^DLead
WillMontrealMale35Perl85ProductLeadTestLead
ShelleyNew YorkFemale27Python80TestLeadCOEArchitect
LucyVancouverFemale57Sales89HR94SalesLead
> !sh hadoop fs -cat /user/output4/000000_0
Michael
Will
Shelley
Lucy

使用技巧:前面已经提到,使用INSERT语句将查询数据写入到文件系统只支持OVERWRITE,但这并不意味着就没有办法进行追加写了。我们可以使用 hive/beeline -e 或 hive/beeline -f 命令并结合linux的管道功能将数据追加到文件中:

追加到本地文件: $hive -e 'select * from employee' >> test
覆盖本地文件: $hive -e 'select * from employee' > test
追加到HDFS文件: $hive -e 'select * from employee'|hdfs dfs -appendToFile - /tmp/test1
覆盖HDFS文件: $hive -e 'select * from employee'|hdfs dfs -put -f - /tmp/test2

示例:将输出追加到HDFS文件中

#执行下面命令2次
$ beeline -u  "jdbc:hive2://localhost:10000/"  -e "select * from test2.employee_internal" | hadoop fs -appendToFile - /tmp/test1
$ beeline -u  "jdbc:hive2://localhost:10000/"  -e "select * from test2.employee_internal" | hadoop fs -appendToFile - /tmp/test1
$ hadoop fs -cat /tmp/test1
+-------------------------+-------------------------------+-------------------------------+---------------------------------+----------------------------------------+
| employee_internal.name  | employee_internal.work_place  | employee_internal.gender_age  | employee_internal.skills_score  |     employee_internal.depart_title     |
+-------------------------+-------------------------------+-------------------------------+---------------------------------+----------------------------------------+
| Michael                 | ["Montreal","Toronto"]        | {"gender":"Male","age":30}    | {"DB":80}                       | {"Product":["Developer^DLead"]}        |
| Will                    | ["Montreal"]                  | {"gender":"Male","age":35}    | {"Perl":85}                     | {"Product":["Lead"],"Test":["Lead"]}   |
| Shelley                 | ["New York"]                  | {"gender":"Female","age":27}  | {"Python":80}                   | {"Test":["Lead"],"COE":["Architect"]}  |
| Lucy                    | ["Vancouver"]                 | {"gender":"Female","age":57}  | {"Sales":89,"HR":94}            | {"Sales":["Lead"]}                     |
+-------------------------+-------------------------------+-------------------------------+---------------------------------+----------------------------------------+
+-------------------------+-------------------------------+-------------------------------+---------------------------------+----------------------------------------+
| employee_internal.name  | employee_internal.work_place  | employee_internal.gender_age  | employee_internal.skills_score  |     employee_internal.depart_title     |
+-------------------------+-------------------------------+-------------------------------+---------------------------------+----------------------------------------+
| Michael                 | ["Montreal","Toronto"]        | {"gender":"Male","age":30}    | {"DB":80}                       | {"Product":["Developer^DLead"]}        |
| Will                    | ["Montreal"]                  | {"gender":"Male","age":35}    | {"Perl":85}                     | {"Product":["Lead"],"Test":["Lead"]}   |
| Shelley                 | ["New York"]                  | {"gender":"Female","age":27}  | {"Python":80}                   | {"Test":["Lead"],"COE":["Architect"]}  |
| Lucy                    | ["Vancouver"]                 | {"gender":"Female","age":57}  | {"Sales":89,"HR":94}            | {"Sales":["Lead"]}                     |
+-------------------------+-------------------------------+-------------------------------+---------------------------------+----------------------------------------+

使用 INSERT…VALUES 语句插入数据到表中

语法

INSERT INTO TABLE tablename [PARTITION (partcol1[=val1], partcol2[=val2] ...)] VALUES values_row [, values_row ...]

说明

  • 在VALUES子句中列出的每一行插入到表tablename表中。
  • VALUES子句必须为表中的每一字段提供值。还不支持将值只插入到某些字段的。若要模拟标准SQL,可以向字段提供的null值。
  • 可以与INSERT … SELECT同样的方式支持动态分区。
  • 如果要插入的表支持ACID并且Hive正在使用一个支持ACID的事务管理器,该操作成功后将自动提交完成。
  • 不支持复杂数据类型 (array, map, struct, union)的文字。这意味着用户不能使用 INSERT INTO…VALUES 子句将数据插入到复杂的数据类型字段中。

示例

示例

> CREATE TABLE students (name VARCHAR(64), 
age INT, 
gpa DECIMAL(3, 2))
CLUSTERED BY (age) INTO 2 BUCKETS 
STORED AS ORC;
 
> INSERT INTO TABLE students
  VALUES ('fred flintstone', 35, 1.28), ('barney rubble', 32, 2.32);
 
> SELECT * FROM students;
+------------------+---------------+---------------+
|  students.name   | students.age  | students.gpa  |
+------------------+---------------+---------------+
| barney rubble    | 32            | 2.32          |
| fred flintstone  | 35            | 1.28          |
+------------------+---------------+---------------+

示例:插入部分字段值 (虽然社区文档中说不支持将数据插入到某些字段,但是实际测试发现可以这样操作,缺失的字段会使用NULL补充)

> INSERT INTO TABLE students(name,age)
  VALUES ('haha', 35), ('hehe', 18);
> SELECT * FROM students;
+------------------+---------------+---------------+
|  students.name   | students.age  | students.gpa  |
+------------------+---------------+---------------+
| barney rubble    | 32            | 2.32          |
| fred flintstone  | 35            | 1.28          |
| hehe             | 18            | NULL          |
| haha             | 35            | NULL          |
+------------------+---------------+---------------+

示例:插入数据到分区表

> CREATE TABLE pageviews (userid VARCHAR(64), 
link STRING, 
came_from STRING)
PARTITIONED BY (datestamp STRING) 
CLUSTERED BY (userid) INTO 256 BUCKETS 
STORED AS ORC;
 
> INSERT INTO TABLE pageviews PARTITION (datestamp = '2014-09-23')
  VALUES ('jsmith', 'mail.com', 'sports.com'), ('jdoe', 'mail.com', null);
  
> SELECT * FROM pageviews;
+-------------------+-----------------+----------------------+----------------------+
| pageviews.userid  | pageviews.link  | pageviews.came_from  | pageviews.datestamp  |
+-------------------+-----------------+----------------------+----------------------+
| jdoe              | mail.com        | NULL                 | 2014-09-23           |
| jsmith            | mail.com        | sports.com           | 2014-09-23           |
+-------------------+-----------------+----------------------+----------------------+

示例:动态分区

> INSERT INTO TABLE pageviews PARTITION (datestamp)
  VALUES ('tjohnson', 'sports.com', 'finance.com', '2014-09-23'), ('tlee', 'finance.com', null, '2014-09-21');
  
> SHOW PARTITIONS pageviews;
+-----------------------+
|       partition       |
+-----------------------+
| datestamp=2014-09-21  |
| datestamp=2014-09-23  |
+-----------------------+

> INSERT INTO TABLE pageviews
  VALUES ('user1', 'hello.com', 'finance.com', '2014-09-23'), ('user2', 'world.com', null, '2014-09-25');
> SHOW PARTITIONS pageviews;
+-----------------------+
|       partition       |
+-----------------------+
| datestamp=2014-09-21  |
| datestamp=2014-09-23  |
| datestamp=2014-09-25  |
+-----------------------+ 

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值