我们在入门篇(十)中介绍了如何将文件中的数据导入一个表中。但实际工作中,更常用的数据插入操作是:将查询出的数据插入一个表中。实现这个操作有两种方法,1:使用CREATE TABLE 表名 AS SELECT……语句;2:使用INSERT语句。下文中我们会分别详细介绍这两种语句的用法。
这个语句的含义是:创建一个表,将后面SELECT语句查询出的数据插入这个新创建的表,字段名和字段数据类型全都和查询出来的数据保持一致。
CREATE TABLE 表名1 AS
SELECT *
FROM 表名2;
后面的select语句可以使用所有合法的查询语句,但只能跟一条HQL语句,因此复杂查询会需要多层子查询嵌套,而且此语句不支持使用with as 语句,所以逻辑复杂时可读性较差。
当数据需求较为复杂时,我们会先建立一些逻辑较为简单的中间临时表,此时使用这个语句会十分方便。
这里我们使用之前博客的语句进行演示,语句如下:
CREATE TABLE temp_test AS
SELECT user_id
,use_cnt
FROM app.t_od_use_cnt
WHERE date_8 = 20190101 limit 5;
运行后查询新建的表temp_test,运行结果如下:
hive (app)> select * from temp_test;
OK
temp_test.user_id temp_test.use_cnt
10004 29
10003 1
10002 23
10001 49
10000 6
Time taken: 0.085 seconds, Fetched: 5 row(s)
INSERT语句可以将查询出的数据插入一个已经建好的表中,查询所得数据的列顺序必须与已存在表中的列顺序一致。
无分区表插入数据的操作一般用于向临时表中插入数据,因为一般只有临时表才不需要分区。
插入数据覆盖表中的原有数据
INSERT overwrite TABLE 表名
SELECT * FROM ……;
插入数据追加在表中原有数据的后面,原有数据不会删除
INSERT INTO TABLE 表名
SELECT * FROM ……;
这里的查询语句同样只能跟一条HQL语句,因此复杂查询会需要多层子查询嵌套,但是,此语句支持使用with as 语句。
当使用WITH AS语句时语法如下:
WITH a
AS (……)
,b
AS (……)
……
INSERT into | overwrite TABLE 表名
SELECT ……;
只要将WITH AS 语句最后的那个SELECT语句放在INSERT语句后即可。
当需要建的临时中间表逻辑较为复杂,使用子查询可读性较差时,可以使用先建一个临时表,然后使用WITH AS ……INSERT语句将查询所得数据插入表中。
这里我们向上文新建的temp_test表插入10条数据,并覆盖之前的数据,这里使用两种语句进行演示,效果是一样的:
INSERT overwrite TABLE temp_test
SELECT user_id
,use_cnt
FROM app.t_od_use_cnt
WHERE date_8 = 20190101 limit 10;
WITH a
AS (
SELECT user_id
,use_cnt
FROM app.t_od_use_cnt
WHERE date_8 = 20190101 limit 10
)
INSERT overwrite TABLE temp_test
SELECT *
FROM a;
查询表temp_test,运行如下:
hive (app)> select * from temp_test;
OK
temp_test.user_id temp_test.use_cnt
10009 40
10008 25
10007 40
10006 12
10005 15
10004 29
10003 1
10002 23
10001 49
10000 6
Time taken: 0.075 seconds, Fetched: 10 row(s)
可见之前的数据已被覆盖,重新插入了10条数据。
在前面的博客我们讲过:Hive 是存储在 HDFS 上的,Hive 的一个分区名对应一个目录名,子分区名就是子目录名,分区字段并不是一个实际字段。在Hive中向分区表中插入数据有两种操作,一种是静态分区插入,一种是动态分区插入,下文我们来分别详细介绍。
2.2.1.1 语法:
[WITH a
AS (……)
,b
AS (……)
……]
INSERT into | overwrite TABLE 表名 PARTITION(分区字段1='xxx',分区字段2='xxx'……)
SELECT ……;
所谓静态分区就是在INSERT语句后 指定要插入的分区的值,如果不写关键字partition直接插入,会报错;
分区字段有几个,插入时就要指定几个分区,否则会报错;
SELECT后的字段不能包含分区字段;否则会报错;
如果分区字段的数据类型为STRING,需要将内容用 ‘ ’ 括起来,如果是数值型则无需 ‘ ’。
2.2.1.2 应用场景:
静态分区插入数据一般用于日常的报表例行化操作中,每天脚本跑昨天的数据,然后指定插入到昨天的分区当中。
2.2.1.3 举例:
建立一个分区表temp_test1,语句如下:
CREATE TABLE temp_test1 (
user_id BIGINT comment '用户id'
,use_cnt INT comment '当日使用次数'
) partitioned BY (
date_8 INT
,hour INT
);
hive (app)>
> WITH a
> AS (
> SELECT user_id
> ,use_cnt
> FROM app.t_od_use_cnt
> WHERE date_8 = 20190101 limit 10
> )
> INSERT overwrite TABLE temp_test1
> SELECT *
> FROM a;
FAILED: SemanticException 8:23 Need to specify partition columns because the destination table is partitioned. Error encountered near token 'temp_test1'
hive (app)>
hive (app)> WITH a
> AS (
> SELECT user_id
> ,use_cnt
> FROM app.t_od_use_cnt
> WHERE date_8 = 20190101 limit 10
> )
> INSERT overwrite TABLE temp_test1 PARTITION(date_8=20190101)
> SELECT *
> FROM a;
FAILED: SemanticException [Error 10006]: Line 8:44 Partition not found '20190101'
hive (app)>
hive (app)> WITH a
> AS (
> SELECT user_id
> ,use_cnt
> FROM app.t_od_use_cnt
> WHERE date_8 = 20190101 limit 10
> )
> INSERT overwrite TABLE temp_test1 PARTITION(hour=1)
> SELECT *
> FROM a;
FAILED: SemanticException Line 0:-1 Partition not found '1'
hive (app)>
正确写法:
INSERT overwrite TABLE temp_test1 PARTITION (
date_8 = 20190101
,hour = 1
)
SELECT user_id
,use_cnt
FROM app.t_od_use_cnt
WHERE date_8 = 20190101 limit 10;
WITH a
AS (
SELECT user_id
,use_cnt
FROM app.t_od_use_cnt
WHERE date_8 = 20190101 limit 10
)
INSERT overwrite TABLE temp_test1 PARTITION (
date_8 = 20190101
,hour = 1
)
SELECT *
FROM a;
二者效果一致。
查询表temp_test1,运行效果如下:
hive (app)> select * from temp_test1 where date_8=20190101 and hour=1;
OK
temp_test1.user_id temp_test1.use_cnt temp_test1.date_8 temp_test1.hour
10009 40 20190101 1
10008 25 20190101 1
10007 40 20190101 1
10006 12 20190101 1
10005 15 20190101 1
10004 29 20190101 1
10003 1 20190101 1
10002 23 20190101 1
10001 49 20190101 1
10000 6 20190101 1
Time taken: 0.34 seconds, Fetched: 10 row(s)
hive (app)> select * from temp_test1 where date_8=20190101;
OK
temp_test1.user_id temp_test1.use_cnt temp_test1.date_8 temp_test1.hour
10009 40 20190101 1
10008 25 20190101 1
10007 40 20190101 1
10006 12 20190101 1
10005 15 20190101 1
10004 29 20190101 1
10003 1 20190101 1
10002 23 20190101 1
10001 49 20190101 1
10000 6 20190101 1
Time taken: 0.112 seconds, Fetched: 10 row(s)
hive (app)> select * from temp_test1 where hour=1;
OK
temp_test1.user_id temp_test1.use_cnt temp_test1.date_8 temp_test1.hour
10009 40 20190101 1
10008 25 20190101 1
10007 40 20190101 1
10006 12 20190101 1
10005 15 20190101 1
10004 29 20190101 1
10003 1 20190101 1
10002 23 20190101 1
10001 49 20190101 1
10000 6 20190101 1
Time taken: 0.073 seconds, Fetched: 10 row(s)
可见在查询分区表时,指定其中一个分区就不会报错,但插入时必须全部指定。
启用 hive 动态分区,首先需要在 hive 会话中设置两个参数:
是否允许动态分区,默认值:false
set hive.exec.dynamic.partition=true;
strict模式是避免全分区字段是动态的,必须有至少一个分区字段是指定有值的,默认值:strict
set hive.exec.dynamic.partition.mode=nonstrict;
另外,如果要插入的分区大于100个小于1000个,需要修改如下参数,否则运行到reduce阶段会报错。
此参数用来限制每个mapper或者reducer的最大动态分区数,默认值是100,此处修改为1000
set hive.exec.max.dynamic.partitions.pernode=1000;
如果要插入的分区大于1000个,需要修改如下参数,否则运行到reduce阶段会报错。
此参数用来限制每个节点的最大动态分区数,默认值是100,此处修改为为符合需求的值即可。
set hive.exec.max.dynamic.partitions.pernode=2000;
此参数用来限制一个DML操作可以创建的动态分区的总分区数,默认值为1000,此处修改为符合需求的值即可。
set hive.exec.max.dynamic.partitions=2000
2.2.2.1 语法:
[WITH a
AS (……)
,b
AS (……)
……]
INSERT into | overwrite TABLE 表名 PARTITION(分区字段1,分区字段2……)
SELECT ……分区字段1,分区字段2……
FROM ……;
动态分区插入数据时只需要写出分区字段名,并不指定具体的分区,然后在下面的查询中最后几个字段必须包含有动态分区的字段,且顺序必须和PARTITION后的分区字段完全一致。
因为hive 会先获取 select 的最后几个位置的分区字段的参数值,然后将这些值填写到 insert 语句 partition 中的几个分区字段中,即动态分区是通过位置来对应分区值的。原始表 select 出来的值和输出 partition 的值的关系仅仅是通过位置来确定的,和名字并没有关系!
2.2.2.2 应用场景:
动态分区插入数据多用于例行化表的回溯数据,比如要将过去30天每天的数据插入一张按天分区的表,如果使用静态分区回溯数据,需要跑30次脚本才行,而使用动态分区则可以一次性全部插入。
2.2.2.3 举例:
hive (app)> INSERT overwrite TABLE temp_test1 PARTITION (
> date_8
> ,hour
> )
> SELECT user_id
> ,use_cnt
> ,date_8
> ,12 hour
> FROM app.t_od_use_cnt
> WHERE date_8 = 20190102 limit 10;
FAILED: SemanticException [Error 10096]: Dynamic partition strict mode requires at least one static partition column. To turn this off set hive.exec.dynamic.partition.mode=nonstrict
hive (app)> INSERT overwrite TABLE temp_test1 PARTITION (
> date_8
> ,hour
> )
> SELECT user_id
> ,use_cnt
> ,date_8
> FROM app.t_od_use_cnt
> WHERE date_8 = 20190102 limit 10;
FAILED: SemanticException [Error 10044]: Line 1:23 Cannot insert into target table because column number/types are different 'hour': Table insclause-0 has 4 columns, but query has 3 columns.
INSERT overwrite TABLE temp_test1 PARTITION (
date_8
,hour
)
SELECT user_id
,use_cnt
,12 hour
,date_8
FROM app.t_od_use_cnt
WHERE date_8 = 20190102 limit 10;
查询表中数据,可以看到虽然字段都有,但最后两个分区字段的内容是错误的:
hive (app)> select * from temp_test1;
OK
temp_test1.user_id temp_test1.use_cnt temp_test1.date_8 temp_test1.hour
10209 39 12 20190102
10208 26 12 20190102
10207 46 12 20190102
10206 39 12 20190102
10205 47 12 20190102
10204 30 12 20190102
10203 18 12 20190102
10202 31 12 20190102
10201 13 12 20190102
10200 46 12 20190102
正确写法:
先设置参数,这些设置只在本次会话中生效,如果想永久生效,可以将其写入.hiverc文件中,具体可参见入门篇(十一)。然后使用动态分区向表temp_test1中插入数据,这里因为表t_od_use_cnt中没有hour字段,所以hour我们直接人为设定一个12:
set hive.exec.dynamic.partition=true;
set hive.exec.dynamic.partition.mode=nonstrict;
INSERT overwrite TABLE temp_test1 PARTITION (
date_8
,hour
)
SELECT user_id
,use_cnt
,date_8
,12 hour
FROM app.t_od_use_cnt
WHERE date_8 = 20190102 limit 10;
查询分区date_8=20190102,hour=12中的数据:
hive (app)> select * from temp_test1 where date_8=20190102 and hour=12;
OK
temp_test1.user_id temp_test1.use_cnt temp_test1.date_8 temp_test1.hour
10209 39 20190102 12
10208 26 20190102 12
10207 46 20190102 12
10206 39 20190102 12
10205 47 20190102 12
10204 30 20190102 12
10203 18 20190102 12
10202 31 20190102 12
10201 13 20190102 12
10200 46 20190102 12
Time taken: 0.077 seconds, Fetched: 10 row(s)
静态分区和动态分区混合使用时,静态分区值必须在动态分区值的前面,否则会报错。
举例:
如果动态分区值必须在静态分区值的前面,报错如下:
hive (app)> set hive.exec.dynamic.partition=true;
hive (app)> set hive.exec.dynamic.partition.mode=nonstrict;
hive (app)> INSERT overwrite TABLE temp_test1 PARTITION (
> date_8
> ,hour=1
> )
> SELECT user_id
> ,use_cnt
> ,12 hour
> FROM app.t_od_use_cnt
> WHERE date_8 = 20190101 limit 10;
FAILED: SemanticException [Error 10094]: Line 2:6 Dynamic partition cannot be the parent of a static partition '1'
正确写法:
set hive.exec.dynamic.partition=true;
set hive.exec.dynamic.partition.mode=nonstrict;
INSERT overwrite TABLE temp_test1 PARTITION (
date_8=20190101
,hour
)
SELECT user_id
,use_cnt
,12 hour
FROM app.t_od_use_cnt
WHERE date_8 = 20190101 limit 10;
能看到这里的同学,就右上角点个赞顺便关注我吧,3Q~