1. select 的顺序要和 insert overwrite 表中的字段顺序保持一致
2. 没有参数的函数可以不用写 (),例如 select current_timestamp;
Home - Apache Hive - Apache Software Foundation
切换数据库
-- 切换为 myhive 数据库
use myhive;
查看所有的数据库
-- 查看所有的数据库
show databases;
查看数据库的详细信息
-- 查看数据库 myhive 的详细信息
desc database myhive;
创建数据库
-- 创建一个名称为 myhive 的数据库
create database if not exists myhive;
数据库表的存放位置
hive
的表存放位置模式是由 hive-site.xml
中的一个属性指定的,该文件存放在 hdfs
中
<property>
<name>hive.metastore.warehouse.dirname>
<value>/user/hive/warehousevalue>
property>
创建数据库并指定 hdfs
存储位置
-- 创建一个名为 myhive2 的数据库,hdfs 的存放路径为 /myhive2
create database myhive2 location '/myhive2';
创建数据库并指定键值对信息
-- 创建一个名为 myhive 的数据库,并指定键值对信息
create database myhive with dbproperties ('owner'='kyle', 'date'='20190120');
查看数据库的详细信息
-- 查看数据库的键值对信息
describe database extended myhive;
-- 查看数据的更多详细信息(没什么区别)
desc database extended myhive;
修改数据库的键值对信息
alter database myhive set dbproperties ('owner'='harry');
删除数据库
-- 删除一个空数据库 myhive ,如果数据库下面有数据表,那么就会报错
drop database myhive;
-- 强制删除数据库 myhive ,包含数据库下面的表一起删除
drop database myhive cascade;
查看表的详细信息
-- 查看表的详细信息
show create table edu_dwd.visit_consult_dwd;
创建表的语法
CREATE [EXTERNAL] TABLE [IF NOT EXISTS] table_name
[(col_name data_type [COMMENT col_comment], ...)]
[COMMENT table_comment]
[PARTITIONED BY (col_name data_type [COMMENT col_comment], ...)]
[CLUSTERED BY (col_name, col_name, ...)
[SORTED BY (col_name [ASC|DESC], ...)] INTO num_buckets BUCKETS]
[ROW FORMAT row_format]
[STORED AS file_format]
[LOCATION hdfs_path]
-- 创建一个指定名字的表:如果相同名字的表已经存在,则抛出异常;用户可以用 IF NOT EXISTS 选项来忽略这个异常
CREATE TABLE
-- 该关键字可以让用户创建一个外部表,在建表的同时指定一个指向实际数据的路径(LOCATION),Hive 创建内部表时,会将数据移动到数据仓库指向的路径;若创建外部表,仅记录数据所在的路径,不对数据的位置做任何改变。在删除表的时候,内部表的元数据和数据会被一起删除,而外部表只删除元数据,不删除数据
EXTERNAL
-- 表示注释,默认不能使用中文
COMMENT
-- 表示使用分区,一个表可以拥有一个或者多个分区,每个分区单独存在一个目录下
PARTITIONED BY
-- 允许用户复制现有的表结构,但是不复制数据
LIKE
-- 可用来指定行分隔符,默认分隔符为 '^A' ,在八进制中为 '\001'
ROW FORMAT DELIMITED
-- 来指定该表数据的存储格式,hive 中,表的默认存储格式为 TextFile
STORED AS SEQUENCEFILE | TEXTFILE | RCFILE | orc | PARQUET
-- 对于每一个表(table)进行分桶(MapReuce中的分区),桶是更为细粒度的数据范围划分。Hive也是 针对某一列进行桶的组织。Hive采用对列值哈希,然后除以桶的个数求余的方式决定该条记录存放在哪个桶当中
CLUSTERED BY
-- 指定表在 HDFS 上的存储路径
LOCATION
-- 指定压缩算法
tblproperties ('orc.compress'='SNAPPY')
Hive 建表的字段类型
分类 | 类型 | 描述 | 字面量示例 |
---|---|---|---|
原始类型 | BOOLEAN | true/false | TRUE |
TINYINT | 1字节的有符号整数 -128~127 | 1Y | |
SMALLINT | 2个字节的有符号整数,-32768~32767 | 1S | |
INT | 4个字节的带符号整数 | 1 | |
BIGINT | 8字节带符号整数 | 1L | |
FLOAT | 字节单精度浮点数1.0 | ||
DOUBLE | 8字节双精度浮点数 | 1.0 | |
DEICIMAL | 任意精度的带符号小数 | 1.0 | |
STRING | 字符串,变长 | “a”,’b’ | |
VARCHAR | 变长字符串 | “a”,’b’ | |
CHAR | 固定长度字符串 | “a”,’b’ | |
BINARY | 字节数组 | 无法表示 | |
TIMESTAMP | 时间戳,毫秒值精度 | 122327493795 | |
DATE | 日期 | ‘2016-03-29’ | |
INTERVAL | 时间频率间隔 | ||
复杂类型 | ARRAY | 有序的的同类型的集合 | array(1,2) |
MAP | key-value,key必须为原始类型,value可以任意类型 | map(‘a’,1,’b’,2) | |
STRUCT | 字段集合,类型可以不同 | struct(‘1’,1,1.0), named_stract(‘col1’,’1’,’col2’,1,’clo3’,1.0) | |
UNION | 在有限取值范围内的一个值 | create_union(1,’a’,63) |
当我们通过 SELECT
查询得到结果,可以很快捷的直接把查询结果作为表的数据进行建表
什么是视图
视图仅仅是存储在数据库中具有关联名称的查询语言的语句,它是以预定义的 SQL
查询形式的表的组合
视图可以包含表的所有行或选定的行
创建视图
-- 创建员工表
CREATE TABLE EMPLOYEE (ID INT, NAME STRING, AGE INT,ADDRESS STRING, SALARY BIGINT);
-- 创建一个视图:用户只能查看 NAME 和 AGE 字段的数据
CREATE VIEW IF NOT EXISTS EMPLOYEE_VIEW AS SELECT NAME,AGE FROM EMPLOYEE;
-- 此时查看 EMPLOYEE 表的数据则只展示 NAME 和 AGE 字段
SELECT * FROM EMPLOYEE;
修改视图
ALTER VIEW EMPLOYEE_VIEW AS SELECT NAME FROM EMPLOYEE;
删除视图
DROP VIEW EMPLOYEE_VIEW;
内部表说明
未被 external
修饰的是内部表( 管理表 managed table
),内部表又称管理表,内部表数据存储的位置由 hive.metastore.warehouse.dir
参数决定(默认:/user/hive/warehouse
),删除内部表会直接删除元数据(metadata)及存储数据,因此内部表不适合和其他工具共享数据
创建一张简单的内部表
-- 使用 myhive 数据库
use myhive;
-- 创建 stu 表
create table stu(id int,name string);
-- 插入数据
insert into stu values (1,"zhangsan");
-- 查询数据
select * from stu;
create table if not exists stu2
(
id int,
name string
) row format delimited fields terminated by '\t' stored as textfile location '/user/stu2';
create table stu3 as
select *
from stu2;
根据已经存在的表创建表
create table stu4 like stu2;
查询表的类型
desc formatted stu2;
删除表(会将 HDFS 的内容也删除)
-- 查看数据库和 HDFS,发现删除内部表之后,所有的内容全部删除
drop table stu2;
外部表说明
在创建表的时候可以指定 external
关键字创建外部表,外部表对应的文件存储在 location
指定的 hdfs
目录下,向该目录添加新文件的同时,该表也会读取到该文件(当然文件格式必须跟表定义的一致)
外部表因为是指定其他的 hdfs
路径的数据加载到表当中来,所以 hive
表会认为自己不完全独占这份数据,所以删除 hive
外部表的时候,数据仍然存放在 hdfs
当中,不会删掉
数据装载 Load
说明
-- 语法
load data [local] inpath '/opt/software/datas/student.txt' overwrite | into table student [partition (partcol1=val1,…)];
-- 参数说明
-- 加载数据
load data
-- 从本地加载数据到 hive 表;否则从 HDFS 加载数据到 hive 表
local
-- 表示加载数据的路径
inpath
-- 表示覆盖表中已有数据,否则表示追加
overwrite
-- 表示加载到哪张表
into table
-- 表示具体的表
student
-- 表示上传到指定分区
partition
数据说明
# student.txt
01 Jackson 1990-01-01 男
02 Harry 1990-12-21 男
03 Ben 1990-05-20 男
04 Jack 1990-08-06 男
05 Rose 1991-12-01 女
06 Lisa 1992-03-01 女
07 Lucy 1989-07-01 女
08 Jim 1990-01-20 女
# teacher.txt
01 Soul
02 Alan
03 Kyle
创建简单的外部表
注意:如果删掉 student
表,hdfs
的数据仍然存在,并且重新创建表之后,表中就直接存在数据了,因为我们的 student
表使用的是外部表,drop table
之后,表当中的数据依然保留在 hdfs
上面了
-- 创建教师表
create external table teacher (t_id string,t_name string) row format delimited fields terminated by '\t';
-- 创建学生表
create external table student (s_id string,s_name string,s_birth string , s_sex string ) row format delimited fields terminated by '\t';
从本地文件系统向表中加载数据
load data local inpath '/opt/software/student.txt' into table student;
从本地文件系统中加载并覆盖已有数据
load data local inpath '/opt/software/student.txt' overwrite into table student;
从 Hdfs 文件系统中向表中加载数据
-- 需要提前将数据添加到这个路径
load data inpath '/test/teacher.txt' into table teacher;
① Array 类型
数据说明
# name 与 locations 之间制表符分隔,locations 中元素之间逗号分隔
Kyle beijing,shanghai,tianjin,hangzhou
Jack changchun,chengdu,wuhan,beijin
建表语句
create table user_address(name string, work_locations array<string>)
row format delimited fields terminated by '\t'
COLLECTION ITEMS TERMINATED BY ',';
导入数据(从本地导入,同样支持从 HDFS
导入)
load data local inpath '/opt/software/work_locations.txt' overwrite into table user_address;
常用查询
-- 查询所有数据
select * from user_address;
-- 查询 loction 数组中第一个元素
select name, work_locations[0] location from user_address;
-- 查询 location 数组中元素的个数
select name, size(work_locations) location from user_address;
-- 查询 location 数组中包含 tianjin 的信息
select * from user_address where array_contains(work_locations,'tianjin');
② map 类型
数据说明
# 字段与字段分隔符: “,”;需要 map 字段之间的分隔符:"#";map内部k-v分隔符:":"
1,Jack,father:Ulrica#mother:Quella#brother:Lilith,28
2,Lucy,father:Gary#mother:Jseph#brother:James,22
3,Kyle,father:Mark#mother:Martin#sister:Mark,29
4,Jim,father:Coco#mother:Charles,26
建表语句
create table user_info(
id int, name string, members map<string,string>, age int
)
row format delimited
fields terminated by ','
COLLECTION ITEMS TERMINATED BY '#'
MAP KEYS TERMINATED BY ':';
导入数据
load data local inpath '/opt/software/user_info.txt' overwrite into table user_info;
常用查询
-- 查询所有数据
select * from user_info;
-- 查询用户的父母信息
select id, name, members['father'] father, members['mother'] mother, age from user_info;
-- 查询 map 的所有 key 信息
select id, name, map_keys(members) as relation from user_info;
-- 查询 map 的所有 value 信息
select id, name, map_values(members) as relation from user_info;
-- 查询 map 中的数量
select id,name,size(members) num from user_info;
-- 查询包含 'brother' 的用户信息
select * from user_info where array_contains(map_keys(members), 'brother');
-- 查询包含 'brother' 的用户信息
select id,name, members['brother'] brother from user_info where array_contains(map_keys(members), 'brother');
③ struct 类型
数据说明
# 字段之间 '#' 分割,第二个字段之间 冒号 分割
192.168.1.1#Kyle:22
192.168.1.2#Lisa:19
192.168.1.3#Jack:18
192.168.1.4#Jim:25
建表语句
create table net_info(
ip string, info struct<name:string, age:int>
)
row format delimited
fields terminated by '#'
COLLECTION ITEMS TERMINATED BY ':';
导入数据
load data local inpath '/opt/software/net_info.txt' into table net_info;
常用查询
-- 查询所有
select * from net_info;
--查询指定信息
select ip, info.name from net_info;
官方文档中描述的区别:Managed vs. External Tables - Apache Hive - Apache Software Foundation
区别\表类型 | 内部表(MANAGED_TABLE) | 外部表(EXTERNAL_TABLE) |
---|---|---|
是否有EXTERNAL修饰 | 否 | 是 |
数据管理 | 数据由 Hive 自身管理 | 数据由 HDFS 管理 |
创建表时 | 将数据移动到数据仓库指向的路径 | 仅记录数据所在的路径,不对数据的位置做任何改变 |
删除表时 | 直接删除元数据(metadata)及存储数据 | 仅会删除元数据,HDFS上的文件并不会被删除 |
官方介绍 EXTERNAL_TABLE:
外部表描述了外部文件的元数据/模式。外部表对应的外部文件不止可以通过 hive
访问,所以这是外部文件的优势:可以和其它工具共享数据。hive
的外部文件可以存放在 ASV
或 HDFS
中,如果外部表中的结构或分区发生了改变,可以通过命令 msck repair table 表名;
刷新元数据信息。删除外部表时,仅删除元数据,外部文件不会被删除。
注意:当我们创建一张外部表,如果没有指定 location
,那我们装载数据后,会发现数据在默认的表数据存放目录( hive.metastore.warehouse.dir
)也会出现一份,这是正常的。如果你指定了 location
,那默认存放目录是不会出现这个表的数据目录的。请注意这个细节,干扰性很强!
官方介绍 MANAGED_TABLE:
内部表的数据默认存放在配置的 hive.metastore.warehouse.dir
路径下,如果建表的时候指定了存放路径就按照自定义的路径存放。删除表的时候,表的元数据和数据都会被删除,且删除的数据会被放在回收站。
查询表的类型
desc formatted student;
修改表的类型
注意:('EXTERNAL'='TRUE')
和 ('EXTERNAL'='FALSE')
为固定写法,区分大小写
-- 将内部表 student 修改为外部表
alter table student set tblproperties('EXTERNAL'='TRUE');
-- 将内部表 student 修改为外部表
alter table student set tblproperties('EXTERNAL'='FALSE');
分区表操作介绍
在大数据中,最常用的一种思想就是分治,我们可以把大的文件切割划分成一个个的小的文件,这样每次操作一个小的文件就会很容易了,同样的道理,在 hive
当中也是支持这种思想的,就是我们可以把大的数据,按照每天,或者每小时进行切分成一个个的小的文件,这样去操作小的文件就会容易得多了
注意:分区字段并不是 hive 表中的字段,分区字段只是我们手动添加的数据标记,分区执行完成后会按照 分区信息在 hdfs 创建分区文件夹,用来存放分区结果数据
创建分区表
-- 创建 score 分区表,按照 month 进行分区
create table score(s_id string,c_id string, s_score int) partitioned by (month string) row format delimited fields terminated by '\t';
创建多个分区的表
-- 创建 score 分区表,按照 year、month、day 进行分区
create table score2 (s_id string,c_id string, s_score int) partitioned by (year string,month string,day string) row format delimited fields terminated by '\t';
加载数据到分区表
-- 只有一个分区的表
load data local inpath '/opt/software/score.csv' into table score partition (month='202006');
-- 多个分区的表
load data local inpath '/opt/software/score.csv' into table score2 partition(year='2020',month='06',day='01');
查看分区信息
show partitions score;
-- 查询分区为 '20180102' 的数据
select * from score where month='20180101'
-- 查询多个分区的数据:'20180102' 和 '20180102' 分区的数据
select * from score where month='20180101' union all select * from score where month='20180102'
添加分区
注:添加分区之后就可以在hdfs文件系统当中看到表下面多了一个文件夹
-- 添加一个分区
alter table score add partition(month='202005');
-- 添加多个分区
alter table score add partition(month='202004') partition(month = '202003');
删除分区
-- 删除分区 202006 下的所有数据
alter table score drop partition(month = '202006');
-- 删除指定分区下的所有数据
alter table edu_dws.score drop partition (year='2019', month='09', day='01');
静态分区和动态分区的定义
根据插入时是否需要手动指定分区可以分为:
① 静态分区:导入数据时需要手动指定分区
② 动态分区:导入数据时,系统可以动态判断目标分区
静态分区需要给出键值对,动态分区只需要给出键就可以了
静态分区
-- 静态分区的建表语句(和动态分区的相同)
create table score(s_id string,c_id string, s_score int)
partitioned by (month string)
row format delimited fields terminated by '\t';
-- 追加插入数据时指定分区
insert into table score_temp partition(year='2021')
select
s.s_id,
s.c_id,
s.s_score
from score s
-- 覆盖插入数据时指定分区
insert overwrite table score_temp partition(year='2021')
select
s.s_id,
s.c_id,
s.s_score
from score s
动态分区
create table score(s_id string,c_id string, s_score int)
partitioned by (year string,month string,day string)
row format delimited fields terminated by '\t';
-- 开启动态分区支持
set hive.exec.dynamic.partition=true;
-- 这个属性默认值是strict,就是要求分区字段必须有一个是静态的分区值。全部动态分区插入,需要设置为nonstrict非严格模式
set hive.exec.dynamic.partition.mode=nonstrict;
-- 数据插入
insert overwrite table score_temp partition(year,month,day)
select
s.s_id,
s.c_id,
s.s_score,
s.s_year as year,
s.s_month as month,
s.s_day as day
from score s
示例:将关联查询的数据插入到动态分区的表中,注意:yearinfo、monthinfo、dayinfo 不是目标表中的字段
insert into table edu_dwd.visit_consult_dwd partition (yearinfo, monthinfo, dayinfo)
select
wce.session_id,
wce.sid,
unix_timestamp(wce.create_time, 'yyyy-MM-dd HH:mm:ss.SSS') as create_time,
wce.seo_source,
wce.ip,
wce.area,
cast(if(wce.msg_count is null, 0, wce.msg_count) as int) as msg_count,
wcte.referrer,
wcte.from_url,
wcte.landing_page_url,
wcte.url_title,
wcte.platform_description,
wcte.other_params,
wcte.history,
substr(wce.create_time, 12, 2) as hourinfo,
quarter(wce.create_time) as quarterinfo,
substr(wce.create_time, 1, 4) as yearinfo,
substr(wce.create_time, 6, 2) as monthinfo,
substr(wce.create_time, 9, 2) as dayinfo
from edu_ods.web_chat_ems wce inner join edu_ods.web_chat_text_ems wcte
on wce.id = wcte.id;
静态分区和动态分区的混用
一张表可同时被静态和动态分区键分区,只是动态分区键需要放在静态分区键的后面(因为HDFS上的动态分区目录下不能包含静态分区的子目录)
-- 注意混用的情况下,静态分区的上层必须也是静态分区
insert overwrite table score_temp partition(year='2021',month,day)
select
s.s_id,
s.c_id,
s.s_score,
s.s_month as month,
s.s_day as day
from score s
有序动态分区
# 启用有序动态分区的优化程序
# 设置为true后,当启用动态分区时,reducer仅随时保持一个记录写入程序,从而降低对 reducer产生的内存压力。但同时也会使查询性能变慢
hive.optimize.sort.dynamic.partition
动态分区的其它属性设置
名称 | 默认值 | 描述 |
---|---|---|
hive.exec.dynamic.partition | false | 设置为 true 用于打开动态分区功能 |
hive.exec.dynamic.partition.mode | strict | 设置为 nonstric 能够让所有的分区动态被设定,否则的话至少需要指定一个分区值 |
hive.exec.max.dynamic.partitions.pemode | 100 | 能被每个 mapper 或者 reducer 创建的最大动态分区的数目,如果一个mapper 或者 reducer 试图创建多于这个值的动态分区数目,会引发错误 |
hive.exec.max.dynamic.partitions | +1000 | 被一条带有动态分区的 SQL 语句所能查u你更加爱你的动态分区的总量,如果超出限制会报出错误 |
hive.exex.max.created.files | 100000 | 全局能被创建文件数目的最大值,专门有一个 Hadoop 计数器来跟踪该值,如果超出会报错 |
① 需求描述
现在有一个文件 score.txt
文件,存放在集群的这个目录下 /scoredatas/month=202006
,这个文件每天都会生成,存放到对应的日期文件夹下面去,文件别人也需要公用,不能移动。需求,创建hive对应的表,并将数据加载到表中,进行数据统计分析,且删除表之后,数据不能删除
② 准备
# 创建指定日期的文件夹
hadoop fs -mkdir -p /scoredatas/month=202006
# 上传文件到文件夹
hadoop fs -put score.txt/scoredatas/month=202006/
③ 创建外部分区表,并指定文件数据存放目录
-- 指定分区字段、文件关联地址、字段分隔符
create external table score4(s_id string, c_id string,s_score int) partitioned by (month string) row format delimited fields terminated by '\t' location '/scoredatas';
④ 对目标表进行修复( 建立文件与表的映射 )
-- 方案一
-- 修复前 执行 'select * from score4' 没有数据
-- 修复后 执行 'select * from score4' 有数据,说明数据加载完成
msck repair table score4;
-- 方案二
-- 增加分区
alter table score4 add partition(month='202006');
分桶表和分区表的区别
分桶和分区两者不干扰,分区表也可以进一步分桶
区别 | Hive 分桶表(MR中的分区表) | Hive 分区表 |
---|---|---|
关键字标识 | clustered by(name string) | partitioned by (name string) |
字段 | 分桶字段存在于数据表中,属于真实的列 | 分区字段形式上存在于数据表中,在查询时会显示到客户端上,并不真正的存在于数据表中,只是对数据做标记,属于伪列 |
实质 | 分桶字段存储在数据表中,按照指定的字段哈希值将文件分成多个 | 分区字段存储在元数据中,按照文件夹对文件进行归类 |
hdfs中 | 分区字段在 hdfs 文件中 | 分区字段不在 hdfs 文件中 |
数据倾斜 | 由于根据字段算的哈希值,数据相对比较平均 | 分区按照列的值进行分割的,容易造成数据倾斜 |
分桶表操作介绍
将数据按照指定的字段进行分成多个桶中去,说白了就是将数据按照字段进行划分,可以将数据按照字段划分到多个文件当中去
为什么要用分桶表
当分区数量过于庞大可能会导致文件系统崩溃时 或 数据集找不到合理的分区字段时,我们就需要使用分桶来解决问题了,分区中的数据可以被进一步拆分成桶,不同于分区对列直接进行拆分,桶往往使用列的哈希值对数据打散,并分发到各个不同的桶中从而完成数据的分桶过程
① 提升数据查询效率
Hive 使用对分桶所用的值进行 Hash,并用 Hash 结果除以桶的个数做取余运算的方式来分桶,保证了每个桶中都有数据,但每个桶中的数据条数不一定相等
如果另外一个表也按照同样的规则分成了一个个小文件,两个表 Join 的时候,就不会扫描整个表,只需要匹配相同分桶的数据即可,从而提升效率,在数据量足够大的情况下,分桶比分区有更高的查询效率,其实就是增加了 Mapper 和 Reducer 的数量
② 数据抽样
在真实的大数据分析过程中,由于数据量较大,开发和自测的过程比较慢,严重影响系统的开发进度。此时就可以使用分桶来进行数据采样。采样使用的是一个具有代表性的查询结果而不是全部结果,通过对采样数据的分析,来达到快速开发和自测的目的,节省大量的研发成本
开启 hive
的桶表功能
如果遇到以下错误:hive configuration hive.enforce.bucketing does not exists,请参考:
hive configuration hive.enforce.bucketing does not exists - 简书 (jianshu.com)
set hive.enforce.bucketing=true;
设置 reduce
的个数
set mapreduce.job.reduces=3;
创建分桶表
-- 创建分桶表,按照 c_id 字段分桶,分桶的数量为 3 ,字段的分隔符为 '\t'
-- HIVE 对 key 的 hash 值除 bucket 个数取余数,保证数据均匀随机分布在所有 bucket 里
create table course (c_id string,c_name string,t_id string) clustered by(c_id) into 3 buckets row format delimited fields terminated by '\t';
注意:load data
导入的数据没有分桶结构,因此分桶表不能通过 load
加载数据,需要使用中间表,先将数据加载到中间表,然后通过中间表的数据写入到分桶表中
创建分桶表的中间表
-- 禁止对分桶表使用 load 操作(避免误操作)
set hive.strict.checks.bucketing = true;
-- 分桶表的中间表,注意:中间表不能分桶
create table course_common (c_id string,c_name string,t_id string) row format delimited fields terminated by '\t';
加载数据到中间表
load data local inpath '/opt/software/course.csv' into table course_common;
-- 覆盖数据写入到分桶表中,此处也可以不写 cluster by
insert overwrite table course select * from course_common cluster by(c_id);
示例:将临时表的数据加载到某个表中(分桶表 + 分区表)
insert overwrite table edu_ods.customer_relationship partition(start_time)
select * from edu_ods.customer_relationship_tmp;
数据抽样
-- 数据抽样格式
select * from table example_table(bucket x out of y on column)
-- x :从哪个 bucket 开始抽取
-- y :表示每隔几个桶取一个分桶
-- x 的值必须要小于 y 的值
-- 示例:example_table 共有 6 个桶
-- 需要抽取的桶的数量:6/2 = 3 个
-- 抽取的桶的序号:1(x)、3(x+y)、5(x+y)
select * from test_buck example_table(bucket 1 out of 2 on id);
表重命名
-- 基本语法
alter table old_table_name rename to new_table_name;
-- 示例:把表score4修改成score5
alter table score4 rename to score5;
-- 查询表结构
desc score;
-- 添加列
alter table score add columns (mycol string, mysco string);
-- 更新列
alter table score change column mysco mysconew int;
-- 增加/更新 列的注释信息
alter table score change column old_id old_id string COMMENT '新的注释信息';
删除表
drop table if exists score;
清空表中的数据
-- 只能清空内部表中的数据
truncate table score;
加载数据之前的配置,按照对应表的类型使用
-- hive 压缩
set hive.exec.compress.intermediate=true;
set hive.exec.compress.output=true;
-- 写入时压缩生效
set hive.exec.orc.compression.strategy=COMPRESSION;
-- 开启动态分区、非严格模式
SET hive.exec.dynamic.partition=true;
SET hive.exec.dynamic.partition.mode=nonstrict;
set hive.exec.max.dynamic.partitions.pernode=10000;
set hive.exec.max.dynamic.partitions=100000;
set hive.exec.max.created.files=150000;
-- 开启分桶
set hive.enforce.bucketing=true;
set hive.enforce.sorting=true;
set hive.optimize.bucketmapjoin = true;
set hive.auto.convert.sortmerge.join=true;
set hive.auto.convert.sortmerge.join.noconditionaltask=true;
-- 开启并行
set hive.exec.parallel=true;
set hive.exec.parallel.thread.number=8;
两个表得瑟数据进行 copy
-- 将 B 表的查询数据插入到 A 表中
insert into table A select id, name from B;
向分区表中加载数据
-- 通过 insert into 方式加载数据
create table score3 like score;
insert into table score3 partition(month ='202007') values ('001','002','100');
-- 通过查询方式加载数据
create table score4 like score;
insert overwrite table score4 partition(month = '202006') select s_id,c_id,s_score from score;
将查询的结果插入到指定的表中,并指定动态分区
insert into table edu_dwd.visit_consult_dwd partition (yearinfo, monthinfo, dayinfo)
select
wce.session_id,
wce.sid,
unix_timestamp(wce.create_time, 'yyyy-MM-dd HH:mm:ss.SSS') as create_time,
wce.seo_source,
wce.ip,
wce.area,
cast(if(wce.msg_count is null, 0, wce.msg_count) as int) as msg_count,
wcte.referrer,
wcte.from_url,
wcte.landing_page_url,
wcte.url_title,
wcte.platform_description,
wcte.other_params,
wcte.history,
substr(wce.create_time, 12, 2) as hourinfo,
quarter(wce.create_time) as quarterinfo,
substr(wce.create_time, 1, 4) as yearinfo,
substr(wce.create_time, 6, 2) as monthinfo,
substr(wce.create_time, 9, 2) as dayinfo
from edu_ods.web_chat_ems wce inner join edu_ods.web_chat_text_ems wcte
on wce.id = wcte.id;
load
方式加载数据
-- 加载数据,表中没有数据
load data inpath '/test/teacher.txt' into table score partition(month='202006');
-- 加载并覆盖已有数据,表中已有数据
load data local inpath '/opt/software/score.txt' overwrite into table score partition(month='202006');
多插入模式加载数据
-- 给 score 表加载数据
load data local inpath '/export/server/hivedatas/score.txt' overwrite into table score partition(month='202006');
-- 创建第一部分表:
create table score_first( s_id string,c_id string) partitioned by (month string) row format delimited fields terminated by '\t' ;
-- 创建第二部分表:
create table score_second(c_id string,s_score int) partitioned by (month string) row format delimited fields terminated by '\t';
-- 分别给第一部分与第二部分表加载数据:一张表的查询结果,加载到两张表中
from score insert overwrite table score_first partition(month='202006') select s_id,c_id insert overwrite table score_second partition(month = '202006') select c_id,s_score;
查询语句中创建表并加载数据(as select)
-- 将查询的结果保存到一张表当中去
create table score5 as select * from score;
创建表时通过 location
指定加载数据路径
-- 1.创建表,并指定在hdfs上的位置
create external table score6 (s_id string,c_id string,s_score int) row format delimited fields terminated by '\t' location '/myscore6';
-- 2.上传数据到hdfs上
hadoop fs -mkdir -p /myscore6
hadoop fs -put score.txt/myscore6;
-- 3.查询数据
select * from score6;
export 导出与 import 导入 hive表数据(内部表操作)
-- 复制表结构
create table teacher2 like teacher;
-- 导出数据
export table teacher to '/export/teacher';
-- 导入数据
import table teacher2 from '/export/teacher';
将查询结果处理后导入指定表中
-- B 表的数据处理后插入 A 表中
insert into A
with order_base as(select * from B)
select * from order_base C group by grouping sets(x,x,x)
导出说明
将 hive
表中的数据导出到其他任意目录,例如 linux
本地磁盘,例如 hdfs
,例如 mysql
等等
insert 导出
-- 将查询的结果导出到本地
insert overwrite local directory '/opt/software/exporthive' select * from score;
-- 将查询的结果格式化导出到本地
insert overwrite local directory '/opt/software/exporthive' row format delimited fields terminated by '\t' collection items terminated by '#' select * from student;
-- 将查询的结果导出到HDFS上(没有local)
insert overwrite directory '/opt/software/exporthive' row format delimited fields terminated by '\t' select * from score;
hive shell 命令导出
-- 基本语法:(hive -f/-e 执行语句或者脚本 > file)
bin/hive -e "select * from myhive.score;" > /export/server/exporthive/score.txt
-- export 导出到 HDFS 上
export table score to '/opt/software/score';
SELECT [ALL | DISTINCT]select_expr, select_expr, ...
FROM table_reference
[WHERE where_condition]
[GROUP BYcol_list]
[HAVING where_condition]
[ORDER BYcol_list]
[CLUSTER BYcol_list
| [DISTRIBUTE BY col_list] [SORT BY col_list]
]
[LIMIT number]
-- 用于全局排序,就是对指定的所有排序键进行全局排序,使用ORDER BY的查询语句,最后会用一个Reduce Task来完成全局排序
ORDER BY
-- 用于分区内排序,即每个 Reduce 任务内排序,sort by 只保证每个 reducer 的输出有序,不保证全局有序
sort by
-- 控制 map 输出结果的分发,按照指定的字段将数据分到不同的 reducer,且分发算法是 hash 散列
distribute by(字段)
-- 除了具有 Distribute by 的功能外,还兼具 sort by 的排序功能
cluster by(字段)
因此,如果分桶和sort字段是同一个时,此时,cluster by = distribute by + sort by
全表查询
select * from score;
选择特定列查询
select s_id ,c_id from score;
列别名
select s_id as myid ,c_id from score;
常用函数
-- 求总行数(count)
select count(1) from score;
-- 求分数的最大值(max)
select max(s_score) from score;
-- 求分数的最小值(min)
select min(s_score) from score;
-- 求分数的总和(sum)
select sum(s_score) from score;
-- 求分数的平均值(avg)
select avg(s_score) from score;
Limit
语句
-- 典型的查询会返回多行数据,LIMIT 子句用于限制返回的行数
select * from score limit 3;
Where
语句
-- 查询出分数大于60的数据
select * from score where s_score > 60;
比较运算符
操作符 | 支持的数据类型 | 描述 |
---|---|---|
A=B | 基本数据类型 | 如果A等于B则返回TRUE,反之返回FALSE |
A<=>B | 基本数据类型 | 如果A和B都为NULL,则返回TRUE,其他的和等号(=)操作符的结果一致,如果任一为NULL则结果为NULL |
A<>B, A!=B | 基本数据类型 | A或者B为NULL则返回NULL;如果A不等于B,则返回TRUE,反之返回FALSE |
A | 基本数据类型 | A或者B为NULL,则返回NULL;如果A小于B,则返回TRUE,反之返回FALSE |
A<=B | 基本数据类型 | A或者B为NULL,则返回NULL;如果A小于等于B,则返回TRUE,反之返回FALSE |
A>B | 基本数据类型 | A或者B为NULL,则返回NULL;如果A大于B,则返回TRUE,反之返回FALSE |
A>=B | 基本数据类型 | A或者B为NULL,则返回NULL;如果A大于等于B,则返回TRUE,反之返回FALSE |
A [NOT] BETWEEN B AND C | 基本数据类型 | 如果A,B或者C任一为NULL,则结果为NULL。如果A的值大于等于B而且小于或等于C,则结果为TRUE,反之为FALSE。如果使用NOT关键字则可达到相反的效果。 |
A IS NULL | 所有数据类型 | 如果A等于NULL,则返回TRUE,反之返回FALSE |
A IS NOT NULL | 所有数据类型 | 如果A不等于NULL,则返回TRUE,反之返回FALSE |
IN(数值1, 数值2) | 所有数据类型 | 使用 IN运算显示列表中的值 |
A [NOT] LIKE B | STRING 类型 | B是一个SQL下的简单正则表达式,如果A与其匹配的话,则返回TRUE;反之返回FALSE。B的表达式说明如下:‘x%’表示A必须以字母‘x’开头,‘%x’表示A必须以字母’x’结尾,而‘%x%’表示A包含有字母’x’,可以位于开头,结尾或者字符串中间。如果使用NOT关键字则可达到相反的效果。 |
A RLIKE B, A REGEXP B | STRING 类型 | B是一个正则表达式,如果A与其匹配,则返回TRUE;反之返回FALSE。匹配使用的是JDK中的正则表达式接口实现的,因为正则也依据其中的规则。例如,正则表达式必须和整个字符串A相匹配,而不是只需与其字符串匹配。 |
查询示例
-- 查询分数等于80的所有的数据
select * from score where s_score = 80;
-- 查询分数在80到100的所有数据
select * from score where s_score between 80 and 100;
-- 查询成绩为空的所有数据
select * from score where s_score is null;
-- 查询成绩是80和90的数据
select * from score where s_score in(80,90);
Like
和 Rlike
-- 使用LIKE运算选择类似的值
-- 选择条件可以包含字符或数字:
-- % 代表零个或多个字符(任意个字符)
-- _ 代表一个字符
-- RLIKE子句是Hive中这个功能的一个扩展,其可以通过Java的正则表达式这个更强大的语言来指定匹配条件
-- 查找以8开头的所有成绩
select * from score where s_score like '8%';
-- 查找第二个数值为9的所有成绩数据
select * from score where s_score like '_9%';
-- 查找成绩中含9的所有成绩数据
select * from score where s_score rlike '[9]';
逻辑运算符
操作符 | 含义 |
---|---|
AND | 逻辑并 |
OR | 逻辑或 |
NOT | 逻辑否 |
-- 查询成绩大于80,并且s_id是01的数据
select * from score where s_score >80 and s_id = '01';
-- 查询成绩大于80,或者s_id 是01的数
select * from score where s_score > 80 or s_id = '01';
-- 查询s_id 不是 01和02的学生
select * from score where s_id not in ('01','02');
Group By
语句
-- 语句通常会和聚合函数一起使用,按照一个或者多个列队结果进行分组,然后对每个组执行聚合操作。注意使用group by分组之后,select后面的字段只能是分组字段和聚合函数
GROUP BY
-- 计算每个学生的平均分数
select s_id ,avg(s_score) from score group by s_id;
-- 计算每个学生最高成绩
select s_id ,max(s_score) from score group by s_id;
-- 按照 create_date_time 字段的切割后的值分组,即按照年分组
-- create_date_time : 2019-11-20 20:47:31
select count(1), substr(create_date_time, 1, 4) from edu_ods.customer_relationship group by substr(create_date_time, 1, 4);
HAVING
语句
-- having与where不同点
-- 1.where针对表中的列发挥作用,查询数据;having针对查询结果中的列发挥作用,筛选数据
-- 2.where后面不能写分组函数,而having后面可以使用分组函数
-- 3.having只用于group by分组统计语句
-- 求每个学生的平均分数
select s_id ,avg(s_score) from score group by s_id;
-- 求每个学生平均分数大于85的人
select s_id ,avg(s_score) avgscore from score group by s_id having avgscore > 85;
右外连接(RIGHT OUTER JOIN)
-- 右外连接:JOIN操作符右边表中符合WHERE子句的所有记录将会被返回
select * from teacher t right join course c on t.t_id = c.t_id;
内连接(INNER JOIN)
-- 内连接:只有进行连接的两个表中都存在与连接条件相匹配的数据才会被保留下来
select * from teacher t inner join course c on t.t_id = c.t_id;
左外连接(LEFT OUTER JOIN)
-- 左外连接:JOIN操作符左边表中符合WHERE子句的所有记录将会被返回
-- 查询老师对应的课程
select * from teacher t left join course c on t.t_id = c.t_id;
满外连接(FULL OUTER JOIN)
-- 满外连接:将会返回所有表中符合WHERE语句条件的所有记录。如果任一表的指定字段没有符合条件的值的话,那么就使用NULL值替代
SELECT * FROM teacher t FULL JOIN course c ON t.t_id = c.t_id ;
多表连接
大多数情况下,Hive 会对每对 JOIN 连接对象启动一个 MapReduce 任务,本例中会首先启动一个 MapReduce job 对表 teacher 和表course 进行连接操作,然后会再启动一个 MapReduce job 将第一个 MapReduce job 的输出和表 score 进行连接操作
-- 注意:连接 n个表,至少需要n-1个连接条件。例如:连接三个表,至少需要两个连接条件。
-- 多表连接查询,查询老师对应的课程,以及对应的分数,对应的学生
select * from teacher t
left join course c
on t.t_id = c.t_id
left join score s
on s.c_id = c.c_id
left join student stu
on s.s_id = stu.s_id;
表关联时副表指的是什么
左连接是指的是右表,右连接时指的是左表
表关联时 where 和 on 的区别
where 限制的是所有的表,不管是内部关联还是外部关联,不管是左表还是右表,都会生效;on 限制只对外连接中的副表生效
Order By-全局排序
注:全局排序,不管 MapTask
有几个,所有的数据都通过一个 ReduceTask
完成,在数据量比较大的情况下,消耗的时间比较长
-- 1.Order By:全局排序,一个reduce
-- 使用 ORDER BY 子句排序
-- ASC(ascend): 升序(默认)
-- DESC(descend): 降序
--2.ORDER BY 子句在SELECT语句的结尾
-- 查询学生的成绩,并按照分数降序排列
SELECT * FROM student s LEFT JOIN score sco ON s.s_id = sco.s_id ORDER BY sco.s_score DESC;
-- 按照分数的平均值排序
select s_id ,avg(s_score) avg from score group by s_id order by avg;
-- 按照学生 id 和平均成绩进行排序
select s_id ,avg(s_score) avg from score group by s_id order by s_id,avg;
对 NULL 值的处理
-- 将 null 值列在最前
select * from student order by student.s_birth nulls first;
-- 将 null 值列在最后
select * from student order by student.s_birth nulls last;
Sort By-每个MapReduce内部局部排序
注意:在每个 ReduceTask
端都会做排序,也就是说保证了局部有序,如果只有一个 ReduceTask
则实际上是全局排序。做完局部排序后,全局排序就比较容易了
-- 每个MapReduce内部进行排序,对全局结果集来说不是排序
-- 设置reduce个数
set mapreduce.job.reduces=3;
-- 查看设置reduce个数
set mapreduce.job.reduces;
-- 查询成绩按照成绩降序排列
select * from score sort by s_score;
-- 将查询结果导入到文件中(按照成绩降序排列)
insert overwrite local directory '/export/server/hivedatas/sort' select * from score sort by s_score;
Distribute By
- 分区排序
注意:Distribute By
排序是控制分区相同的数据由一个 ReduceTask
执行,然后进行排序,理论上数据的有序是全局有效的,因为只是对分区来说的
-- Distribute By:类似MR中partition,进行分区,结合sort by使用。
-- 注意,Hive要求DISTRIBUTE BY语句要写在SORT BY语句之前。
-- 对于distribute by进行测试,一定要分配多reduce进行处理,否则无法看到distribute by的效果
-- 先按照学生 id 进行分区,再按照学生成绩进行排序
-- 设置 reduce 的个数,将我们对应的 s_id 划分到对应的 reduce 当中去
set mapreduce.job.reduces=7;
-- 通过distribute by进行数据的分区
insert overwrite local directory '/opt/software/sort' select * from score distribute by s_id sort by s_score;
Cluster By
-- 当 distribute by 和 sort by 字段相同时,可以使用 cluster by 方式
-- cluster by除了具有distribute by的功能外还兼具sort by的功能。但是排序只能是倒序排序,不能指定排序规则为ASC或者DESC
select * from score cluster by s_ sid;
select * from score distribute by s_id sort by s_id;
用于合并两个或多个 select 语句的结果集,每个 select 语句查询的字段的数量、字段的类型、字段的顺序必须相同
① union
-- 会屏蔽掉重复的数据
SELECT id,name,age FROM customer_1
UNION
SELECT id,name,age FROM customer_2
② union all
-- 不会屏蔽掉重复的数据
SELECT id,name,age FROM customer_1
UNION
SELECT id,name,age FROM customer_2
with as 说明
with as 也叫做子查询部分,首先定义一个 sql 片段,该 sql 片段会被整个 sql 语句所用到,可以提高 sql 语句的可读性更高些
怎么用
-- 定义 2 个 sql 片段:t1,t2
WITH t1 AS (
SELECT *
FROM carinfo
),
t2 AS (
SELECT *
FROM car_blacklist
)
SELECT *
FROM t1, t2
-- 1、查询"01"课程比"02"课程成绩高的学生的信息及课程分数
SELECT a.* ,b.s_score AS 01_score,c.s_score AS 02_score FROM
student a
LEFT JOIN score b ON a.s_id=b.s_id AND b.c_id='01'
LEFT JOIN score c ON a.s_id=c.s_id AND c.c_id = '02' WHERE b.s_score>c.s_score;
-- 2、查询"01"课程比"02"课程成绩低的学生的信息及课程分数
SELECT a.* ,b.s_score AS 01_score,c.s_score AS 02_score FROM
student a LEFT JOIN score b ON a.s_id=b.s_id AND b.c_id='01'
JOIN score c ON a.s_id=c.s_id AND c.c_id='02' WHERE b.s_score<c.s_score;
-- 3、查询平均成绩大于等于60分的同学的学生编号和学生姓名和平均成绩
SELECT b.s_id,b.s_name,ROUND(AVG(a.s_score),2) AS avg_score FROM
student b
JOIN score a ON b.s_id = a.s_id
GROUP BY b.s_id,b.s_name HAVING ROUND(AVG(a.s_score),2)>=60;
-- 4、查询平均成绩小于60分的同学的学生编号和学生姓名和平均成绩(包括有成绩的和无成绩的)
SELECT b.s_id,b.s_name,ROUND(AVG(a.s_score),2) AS avg_score FROM
student b
LEFT JOIN score a ON b.s_id = a.s_id
GROUP BY b.s_id,b.s_name HAVING ROUND(AVG(a.s_score),2)<60
UNION ALL
SELECT a.s_id,a.s_name,0 AS avg_score FROM
student a
WHERE a.s_id NOT IN (
SELECT DISTINCT s_id FROM score);
-- 5、查询所有同学的学生编号、学生姓名、选课总数、所有课程的总成绩
SELECT a.s_id,a.s_name,COUNT(b.c_id) AS sum_course,SUM(b.s_score) AS sum_score FROM
student a
LEFT JOIN score b ON a.s_id=b.s_id
GROUP BY a.s_id,a.s_name;
-- 6、查询"李"姓老师的数量
select count(t_id) from teacher where t_name like '李%';
-- 7、查询学过"张三"老师授课的同学的信息
SELECT a.*
FROM student a LEFT JOIN score b ON a.s_id = b.s_id WHERE b.c_id IN (
SELECT c.c_id
FROM course c LEFT JOIN teacher t ON c.t_id = t.t_id WHERE t.t_name = '张三'
) ;
-- 8、查询没学过"张三"老师授课的同学的信息
SELECT s.*
FROM student s LEFT JOIN (
SELECT a.s_id
FROM student a LEFT JOIN score b ON a.s_id = b.s_id WHERE b.c_id IN (
SELECT c.c_id
FROM course c LEFT JOIN teacher t ON c.t_id = t.t_id WHERE t.t_name = '张三'
)
) ss ON s.s_id = ss.s_id WHERE ss.s_id IS NULL;
-- 9、查询学过编号为"01"并且也学过编号为"02"的课程的同学的信息
select a.* from
student a,score b,score c
where a.s_id = b.s_id and a.s_id = c.s_id and b.c_id='01' and c.c_id='02';
-- 10、查询学过编号为"01"但是没有学过编号为"02"的课程的同学的信息
SELECT qq.*
FROM (
SELECT s.*
FROM student s LEFT JOIN score sco ON s.s_id = sco.s_id LEFT JOIN course c ON sco.c_id = c.c_id WHERE c.c_id='01'
) qq
LEFT JOIN (
SELECT stu.*
FROM student stu LEFT JOIN score mysco ON stu.s_id = mysco.s_id LEFT JOIN course cou ON mysco.c_id = cou.c_id WHERE cou.c_id='02'
) pp
ON qq.s_id = pp.s_id WHERE pp.s_id IS NULL;
-- 11、查询没有学全所有课程的同学的信息
SELECT ss.s_id
FROM (
SELECT stu.s_id,COUNT(stu.s_id) AS num
FROM student stu LEFT JOIN score sco ON stu.s_id = sco.s_id LEFT JOIN course cou ON sco.c_id = cou.c_id
GROUP BY stu.s_id
) ss WHERE ss.num < 3
-- 12、查询至少有一门课与学号为"01"的同学所学相同的同学的信息
SELECT stu.*
FROM student stu LEFT JOIN
(
SELECT s.s_id
FROM score s WHERE s.c_id IN(
SELECT c_id FROM score WHERE s_id = '01'
)GROUP BY s_id
) pp ON stu.s_id = pp.s_id WHERE pp.s_id IS NOT NULL;
语法结构
# 语法结构
hive [-hiveconf x=y]* [<-i filename>]* [<-f filename>|<-e query-string>] [-S]
# 参数说明
# 从文件初始化HQL
-i
# 从命令行执行指定的HQL
-e
# 执行HQL脚本
-f
# 输出执行的HQL语句到控制台
-v
# connect to Hive Server on port number
-p <port>
# se this to set hive/hadoop configuration variables. 设置hive运行时候的参数配置
-hiveconf x=y U
语法示例
# 从命令执行指定的 HQL
hive -e 'show databases'
# 执行 HQL 脚本
# 脚本中的内容:show databases;
hive -f /opt/hive-0.13.1/hfile.sql
参数配置方式说明
开发 Hive
应用时,不可避免地需要设定 Hive
的参数,设定 Hive
的参数可以调优 HQL
代码的执行效率,或帮助定位问题,参数的设定方式有以下几种:
① 配置文件
② 命令行参数
③ 参数声明
配置文件设定参数(全局有效)
用户自定义配置会覆盖默认配置另外,Hive
也会读入 Hadoop
的配置,因为 Hive
是作为 Hadoop
的客户端启动的,Hive
的配置会覆盖 Hadoop
的配置
# 默认的配置文件
$HIVE_CONF_DIR/hive-default.xml
# 用户自定义的配置文件
$HIVE_CONF_DIR/hive-site.xml
命令行设定参数(Session 或 Server有效)
启动 Hive
(客户端或Server方式)时,可以在命令行添加 -hiveconf param=value
来设定参数,例如
# 设定仅对本次启动的Session(对于Server方式启动,则是所有请求的Sessions)有效
hive -hiveconf hive.root.logger=INFO,console
参数申明设定参数(Session有效)
HQL
中使用 SET
关键字设定参数
set mapred.reduce.tasks=100;
上述三种设定方式的优先级依次递增,即参数声明覆盖命令行参数,命令行参数覆盖配置文件设定
注意某些系统级的参数,例如 log4j
相关的设定,必须用前两种方式设定,因为那些参数的读取在 Session
建立以前已经完成了
优先级:参数声明 > 命令行参数 > 配置文件参数(hive)