Hive零基础从入门到实战 入门篇(二十一) HiveQL:数据插入

目录

 

前言

1. CREATE TABLE 表名 AS SELECT……

2. INSERT

2.1 无分区表插入数据

2.2 分区表插入数据

2.2.1 静态分区插入

2.2.2 动态分区插入

2.2.3 静态分区和动态分区混合使用


 

前言

我们在入门篇(十)中介绍了如何将文件中的数据导入一个表中。但实际工作中,更常用的数据插入操作是:将查询出的数据插入一个表中。实现这个操作有两种方法,1:使用CREATE TABLE 表名 AS SELECT……语句;2:使用INSERT语句。下文中我们会分别详细介绍这两种语句的用法。

 

1. CREATE TABLE 表名 AS SELECT……

这个语句的含义是:创建一个表,将后面SELECT语句查询出的数据插入这个新创建的表,字段名和字段数据类型全都和查询出来的数据保持一致。

 

1.1 语法

CREATE TABLE 表名1 AS
SELECT *
FROM 表名2;

后面的select语句可以使用所有合法的查询语句,但只能跟一条HQL语句,因此复杂查询会需要多层子查询嵌套,而且此语句不支持使用with as 语句,所以逻辑复杂时可读性较差。

 

1.2 应用场景

当数据需求较为复杂时,我们会先建立一些逻辑较为简单的中间临时表,此时使用这个语句会十分方便。

 

1.3 举例

这里我们使用之前博客的语句进行演示,语句如下:

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)

2. INSERT

INSERT语句可以将查询出的数据插入一个已经建好的表中,查询所得数据的列顺序必须与已存在表中的列顺序一致。

2.1 无分区表插入数据

无分区表插入数据的操作一般用于向临时表中插入数据,因为一般只有临时表才不需要分区。 

2.1.1 语法

插入数据覆盖表中的原有数据

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语句后即可。

 

2.1.2 应用场景

当需要建的临时中间表逻辑较为复杂,使用子查询可读性较差时,可以使用先建一个临时表,然后使用WITH AS ……INSERT语句将查询所得数据插入表中。

 

2.1.3 举例

这里我们向上文新建的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 AS插入
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条数据。

 

2.2 分区表插入数据

在前面的博客我们讲过:Hive 是存储在 HDFS 上的,Hive 的一个分区名对应一个目录名,子分区名就是子目录名,分区字段并不是一个实际字段。在Hive中向分区表中插入数据有两种操作,一种是静态分区插入,一种是动态分区插入,下文我们来分别详细介绍。

2.2.1 静态分区插入

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
      );
  • 如果对分区表插入数据如果不写关键字partition直接插入,会报错,如下:
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)> 
  • 如果只指定date_8或者hour其中一个分区插入数据,会报错,如下:
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)

可见在查询分区表时,指定其中一个分区就不会报错,但插入时必须全部指定。

 

2.2.2 动态分区插入

启用 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)

2.2.3 静态分区和动态分区混合使用

静态分区和动态分区混合使用时,静态分区值必须在动态分区值的前面,否则会报错。

举例:

如果动态分区值必须在静态分区值的前面,报错如下:

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~

你可能感兴趣的:(Hive零基础从入门到实战 入门篇(二十一) HiveQL:数据插入)