目录
Hive是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表,并提供类SQL查询功能。
- 直接使用hadoop所面临的问题
人员学习成本太高
项目周期要求太短
MapReduce实现复杂查询逻辑开发难度太大
- 为什么要使用Hive
操作接口采用类SQL语法,提供快速开发的能力。
避免了去写MapReduce,减少开发人员的学习成本。
扩展功能很方便。
- 可扩展
Hive可以自由的扩展集群的规模,一般情况下不需要重启服务。
- 延展性
Hive支持用户自定义函数,用户可以根据自己的需求来实现自己的函数。
- 容错
良好的容错性,节点出现问题SQL仍可完成执行。
Jobtracker是hadoop1.x中的组件,它的功能相当于: Resourcemanager+AppMaster
TaskTracker 相当于: Nodemanager + yarnchild
- 用户接口:包括 CLI、JDBC/ODBC、WebGUI。
- 元数据存储:通常是存储在关系数据库如 mysql , derby中。
- 解释器、编译器、优化器、执行器。
- 用户接口主要由三个:CLI、JDBC/ODBC和WebGUI。其中,CLI为shell命令行;JDBC/ODBC是Hive的JAVA实现,与传统数据库JDBC类似;WebGUI是通过浏览器访问Hive。
- 元数据存储:Hive 将元数据存储在数据库中。Hive 中的元数据包括表的名字,表的列和分区及其属性,表的属性(是否为外部表等),表的数据所在目录等。
- 解释器、编译器、优化器、执行器完成 HQL 查询语句从词法分析、编译、优化以及查询计划的生成。生成的查询计划存储在 HDFS 中,并在随后由 MapReduce 调用执行。
- Hive与Hadoop的关系
Hive利用HDFS存储数据,利用MapReduce查询数据
-
- Hive与传统数据库对比
总结:hive具有sql数据库的外表,但应用场景完全不同,hive只适合用来做批量数据统计分析
1、Hive中所有的数据都存储在 HDFS 中,没有专门的数据存储格式(可支持Text,SequenceFile,ParquetFile,RCFILE等)
2、只需要在创建表的时候告诉 Hive 数据中的列分隔符和行分隔符,Hive 就可以解析数据。
3、Hive 中包含以下数据模型:DB、Table,External Table,Partition,Bucket。
- db:在hdfs中表现为${hive.metastore.warehouse.dir}目录下一个文件夹
- table:在hdfs中表现所属db目录下一个文件夹
- external table:与table类似,不过其数据存放位置可以在任意指定路径
- partition:在hdfs中表现为table目录下的子目录
- bucket:在hdfs中表现为同一个表目录下根据hash散列之后的多个文件
1.6Hive安装
1.1 内嵌 Derby 版本
上传 Hive 安装包 apache-hive-1.2.1-bin.tar.gz
解压 Hive 安装包 tar -zxvf apache-hive-1.2.1-bin.tar.gz
进入到 Hive 安装目录bin目录下,运行Hive脚本:
[shiny@hadoop01 bin]$ ./hive
如下图所示:
注意:这时候出现一个错误:Terminal initialization failed; falling back to unsupported,这是因为Hadoop(/gs/hive/lib)集群的jline-0.9.94.jar包版本过低,替换成/gs/hadoop/share/hadoop/yarn/lib中的jline-2.12.jar包即可。
记住:所有的节点都得替换,不是只有安装Hive的节点。
1.2 外置 MySQL 版本
1.安装 MySQL【见之前的文档】
2.增加远程登录权限,执行以下两个命令:
mysql> GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '123456' WITH GRANT OPTION;
mysql> FLUSH PRIVILEGES;
命令释义: grant 权限 1,权限 2,…权限 n on 数据库名称.表名称 to 用户名@用户地址 identified by '密码';
3.修改Hive的配置文件,使用命令:
[shiny@hadoop01 conf]# vim hive-site.xml
如下图所示:
将以下内容写入配置文件hive-site.xml中:
<configuration>
<property>
<name>javax.jdo.option.ConnectionURL</name>
<value>jdbc:mysql://hadoop01:3306/hivedb?createDatabaseIfNotExist=true</value>
<description>JDBC connect string for a JDBC metastore</description>
</property>
<property>
<name>javax.jdo.option.ConnectionDriverName</name>
<value>com.mysql.jdbc.Driver</value>
<description>Driver class name for a JDBC metastore</description>
</property>
<property>
<name>javax.jdo.option.ConnectionUserName</name>
<value>root</value>
<description>username to use against metastore database</description>
</property>
<property>
<name>javax.jdo.option.ConnectionPassword</name>
<value>123456</value>
<description>password to use against metastore database</description>
</property>
</configuration>
可选配置:
<property>
<name>hive.metastore.warehouse.dir</name>
<value>hdfs://hadoop01:9000/user/hive/warehouse</value>
<description>hive default warehouse, if nessecory, change it</description>
</property>
- 将MySQL的驱动包(mysql-connector-java-5.1.40-bin.jar)放置到Hive安装目录lib目录下。
- 配置环境变量
export HIVE_HOME=/gs/hive
export PATH=$PATH:$HIVE_HOME/bin
让配置文件生效
[shiny@hadoop01 apache-hive-1.2.1-bin]$ source ~/.bash_profile
- 验证Hive,使用如下命令:
[shiny@hadoop01 bin]$ hive --help
运行结果:
代表Hive安装成功!!!!
1.7Hive的连接方式
2.1 CLI
进入Hive安装目录的bin目录下,直接输入命令:
[root@hadoop01 bin]# ./hive
启动成功之后,就可以做Hive的相关操作了,如下图所示:
2.2 HiveServer2/beeline
hive启动hive thrift服务端,使用命令:
[shiny@hadoop01 bin]$ hiveserver2
如下图所示:
启动为前台,所以我们需要再打开一个终端运行以下命令:
[shiny@hadoop01 ~]$ beeline
按图中的步骤进行,默认端口为10000,可通过hive.server2.thrift.port参数调整。如下图所示:
2.3 Web UI
这种方式相对前两种来说,用得比较少.
1.8 Dbeaver连接hive
18.1 启动监听
先在shell上打开hive的服务监听(建议使用会话,因为启动之后,界面不会自动退出)
18.2 查看监听
过10s-60s会有监听10000端口,此时就可以使用dbeaver连接了
18.3 dbeaver连接hive
先连上互联网,因为需要自动下载驱动
写上服务器ip,端口默认是10000,如果自己修改了端口,就自行修改一下
用户名和密码如果设置了就写上,没设置就不用写
点击测试链接,会自动下载驱动,并进行连接
2.Hive基本操作
1、DDL操作
DDL(Data Definition Language)数据定义语言 是 Hive SQL语句的的一个子集,它通过creating、deleting、or altering模式对象(数据库、表、分区、视图、Buckets)来描述Hive的数据结构。大部分的DDL关键词为CREATE、 DROP或者ALTER,Hive DDL的语法类似于SQL,所以对有数据库基础的人员看起来会异常亲切。
Hive QL 对 SQL 语句的支持:
- SELECT * FROM db.tablename
- SELECT COUNT(DISTINCT uid) FROM db.tablename
- 支持 SELECT、UNION ALL、JOIN(LEFT、RIGHT、FULL JOIN)、LIKE、WHERE各种聚合函数、支持 json 解析
- UDF(User Defined Function) / UDAF
- 不支持 UPDATE 和 DELETE
- Hive 支持 IN/EXISTS, Hive 使用 SEMI JOIN 的方式来代替实现,而且效率更高
1.1 创建表
1.1.1 建表语法结构
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]
1.1.2 建表语句解释
CREATE TABLE:创建一个指定名字的表。如果相同名字的表已经存在,则抛出异常;用户可以用 IF NOT EXISTS 选项来忽略这个异常。
EXTERNAL:可以让用户创建一个外部表,在建表的同时指定一个指向实际数据的路径(LOCATION)。 Hive创建内部表时,会将数据移动到数据仓库指向的路径;若创建外部表,仅记录数据所在的路径,不对数据的位置做任何改变。在删除表的时候,内部表的元数据和数据会被一起删除,而外部表只删除元数据,不删除数据。
PARTITIONED:在 Hive Select 查询中一般会扫描整个表内容,会消耗很多时间做没必要的工作。有时候只需要扫描表中关心的一部分数据,因此建表时引入了 partition 概念。每个表可以拥有一个或者多个分区,每个分区以文件夹的形式单独存在表文件夹的目录下,分区是以字段的形式在表结构中存在,通过 desc table 命令可以查看到字段存在,只是该字段不存放实际的数据内容,仅仅是分区的表示。分区建表分为 2 种,一种是单分区,也就是说在表文件夹目录下只有一级文件夹目录。另外一种是多分区,表文件夹下出现多文件夹嵌套模式。
CLUSTERED BY:对于每一个表(table)或者分区, Hive可以进一步组织成桶,也就是说桶是更为细粒度的数据范围划分。 Hive也是针对某一列进行桶的组织。 Hive采用对列值哈希,然后除以桶的个数求余的方式决定该条记录存放在哪个桶当中。把表(或者分区)组织成桶(Bucket)有两个理由:
(1)获得更高的查询处理效率。桶为表加上了额外的结构,Hive在处理有些查询时能利用这个结构。具体而言,连接两个在(包含连接列的)相同列上划分了桶的表,可以使用 Map 端连接(Map-side join)高效的实现。比如 JOIN 操作。对于 JOIN 操作两个表有一个相同的列,如果对这两个表都进行了桶操作。那么将保存相同列值的桶进行 JOIN 操作就可以,可以大大较少 JOIN 的数据量。
(2)使取样(sampling)更高效。在处理大规模数据集时,在开发和修改查询的阶段,如果能在数据集的一小部分数据上试运行查询,会带来很多方便。
ROW FORMAT:
ROW FORMAT
DELIMITED [FIELDS TERMINATED BY char]
[COLLECTION ITEMS TERMINATED BY char]
[MAP KEYS TERMINATED BY char]
[LINES TERMINATED BY char]
| SERDE serde_name [WITH SERDEPROPERTIES (property_name=property_value,
operty_name=property_value, ...)]
用户在建表的时候可以自定义 SerDe 或者使用自带的 SerDe。如果没有指定 ROW FORMAT 或者 ROW FORMAT DELIMITED,将会使用自带的SerDe。在建表的时候,用户还需要为表指定列,用户在指定表的列的同时也会指定自定义的 SerDe,Hive 通过 SerDe 确定表的具体的列的数据。
STORED AS SEQUENCEFILE|TEXTFILE|RCFILE:
如果文件数据是纯文本,可以使用 STORED AS TEXTFILE。如果数据需要压缩,使用STORED AS SEQUENCEFILE。
LOCATION:指定数据文件存放的 HDFS 目录。
LIKE:允许用户复制现有的表结构,但是不复制数据。
COMMENT:可以为表与字段增加描述。
1.1.3 Hive建表示例
CREATE TABLE page_view
(viewTime INT comment '字段注释', userid BIGINT , page_url STRING, referrer_url STRING, ip STRING
COMMENT '字段注释')
COMMENT '表注释'
PARTITIONED BY(datetime STRING, country STRING)
CLUSTERED BY(userid) SORTED BY(viewTime) INTO 20 BUCKETS
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
COLLECTION ITEMS TERMINATED BY '\t'
MAP KEYS TERMINATED BY '\t'
STORED AS TEXTFILE;
执行命令查看表结构:
hive> DESC FORMATTED page_view;
查询结果如下图所示:
1.1.4 具体示例
创建内部表
hive> CREATE TABLE student(
> id INT,
> name STRING,
> age INT)
> ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
> STORED AS TEXTFILE
运行结果如下图所示:
HDFS上的显示结果:
创建外部表
hive> CREATE EXTERNAL TABLE student2(
> id INT,
> name STRING,
> age INT)
> ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
> LOCATION '/user/hive/warehouse/shiny.db/student2';
运行结果如下图所示:
HDFS上的显示结果:
创建分区表
hive> CREATE TABLE student3(
> id INT,
> name STRING,
> age INT)
> PARTITIONED BY(sex STRING)
> ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
> STORED AS TEXTFILE;
运行结果如下图所示:
插入数据:
总共有2份数据,student1.txt的数据为:
[shiny@hadoop01 test]$ cat student1.txt
1001 shiny 23
1002 cendy 22
1003 angel 23
1009 ella 21
1012 eva 24
student2.txt中的数据为:
[shiny@hadoop01 test]$ cat student2.txt
1005 bob 24
1006 mark 23
1007 leo 22
1011 JACK 23
1014 JAMES 24
插入女分区数据:
hive> LOAD DATA LOCAL INPATH '/home/shiny/Desktop/test/student1.txt'
> INTO TABLE student3 PARTITION(SEX='female');
插入男分区数据:
hive> LOAD DATA LOCAL INPATH '/home/shiny/Desktop/test/student2.txt'
> INTO TABLE student3 PARTITION(SEX='male');
查询student3的内容:
查询表分区:
hive> SHOW PARTITIONS student3;
HDFS上的显示结果:
创建分桶表
hive> CREATE TABLE student4(
> id INT,
> name STRING,
> age INT)
> CLUSTERED BY(id) SORTED BY(id DESC) INTO 3 BUCKETS
> ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t';
运行结果如下图所示:
1.2 修改表
重命名表
ALTER TABLE table_name RENAME TO new_table_name;
示例
增加/删除/改变/替换列
ALTER TABLE table_name ADD COLUMNS (col_spec[, col_spec ...])
ALTER TABLE table_name DROP [COLUMN] column_name【drop不支持】
ALTER TABLE table_name CHANGE c_name new_name new_type [FIRST|AFTER c_name]
ALTER TABLE table_name REPLACE COLUMNS (col_spec[, col_spec ...])
注意:
(1)删除列DROP操作版本不支持,可以使用REPLACE操作替代。
(2)ADD 是代表新增一字段,字段位置在所有列后面(PARTITION 列前), REPLACE 则是表示替换表中所有字段。
ADD示例
CHANGE示例
REPLACE示例
增加/删除分区
-增加分区语法结构
ALTER TABLE table_name ADD [IF NOT EXISTS]
partition_spec [ LOCATION 'location1' ]
partition_spec [ LOCATION 'location2' ]
...
partition_spec:
: PARTITION (partition_col = partition_col_value, partition_col = partiton_col_value, ...)
- 删除分区语法结构:
ALTER TABLE table_name DROP partition_spec, partition_spec,...
增加分区示例:
ALTER TABLE student3 ADD
PARTITION(province='shandong')
LOCATION '/user/hive/warehouse/shiny.db/student/22'
PARTITION(province='beijing')
LOCATION '/user/hive/warehouse/shiny.db/student/beijing'
PARTITION(province='tianjin')
LOCATION '/user/hive/warehouse/shiny.db/student/tianjin';
上面的示例运行之后不会成功,会提示错误信息:
这是由于在新建表的时候,并没有创建分区列province,所以只有在存在分区列的表上执行增加分区的操作,才会成功。
之前我们在创建表student3时创建了分区列sex,所以我们可以执行以下语句:
ALTER TABLE student3 ADD
PARTITION(sex='weizhi')
LOCATION '/user/hive/warehouse/shiny.db/student3/sex=weizhi';
运行结果如下图所示:
删除分区示例:
ALTER TABLE student3 DROP
PARTITION(sex='weizhi');
运行结果如下图所示:
1.3 删除表
语法结构:
DROP TABLE [IF EXISTS] table_name;
示例:
1.4 常用显示命令
SHOW TABLES; ##显示当前数据库中所有的表
SHOW DATABASES; ##显示所有的数据库
SHOW PARTITIONS table_name; ##显示分区信息
SHOW FUNCTIONS; ##显示所有函数
DESC [FORMATTED] table_name; ##显示表结构信息
2、DML操作
DML(Data Manipulation Language)数据操纵语言 命令使用户能够查询数据库以及操作已有数据库中的数据。
DML主要是对Hive 表中的数据进行操作
2.1 LOAD装载数据
语法结构:
LOAD DATA [LOCAL] INPATH 'filepath' [OVERWRITE] INTO TABLE tablename
[PARTITION(partcol1=val1, partcol2=val2 ...)]
建表语句解释:
- LOAD 操作只是单纯的复制(linux)/移动(hadoop)操作,将数据文件移动到 Hive 表对应的位置。
(2)filepath:
相对路径,例如: project/data1
绝对路径,例如: /user/hive/project/data1
包含模式的完整 URI,列如:
hdfs://namenode_host:9000/user/hive/project/data1
(3)LOCAL 关键字:
如果指定了 LOCAL, LOAD 命令会去查找本地文件系统中的 filepath。
如果没有指定 LOCAL 关键字,则根据 inpath 中的 URI 查找文件。
注意: URI 是指 HDFS 上的路径,分简单模式和完整模式两种,例如:
简单模式: /user/hive/project/data1
完整模式: hdfs://namenode_host:9000/user/hive/project/data1
(4)OVERWRITE 关键字
如果使用了 OVERWRITE 关键字,则目标表(或者分区)中的内容会被删除,然后再将 filepath 指向的文件/目录中的内容添加到表/分区中。如果目标表(分区)已经有一个文件,并且文件名和 filepath 中的文件名冲突,那么现有的文件会被新文件所替代。
加载本地数据示例:
LOAD DATA LOCAL INPATH '/home/shiny/Desktop/test/student1.txt'
INTO TABLE student3 PARTITION(SEX='female');
运行结果如下图所示:
HDFS上的显示结果:
加载HDFS数据示例:
LOAD DATA INPATH '/user/hive/warehouse/shiny.db/student3/sex=female/student1.txt'
INTO TABLE student3 PARTITION(SEX='male');
运行结果如下图所示:
HDFS上的显示结果:
OVERWRITE关键字使用示例:
LOAD DATA LOCAL INPATH '/home/shiny/Desktop/test/student2.txt'
OVERWRITE INTO TABLE student3 PARTITION(SEX='male');
运行结果如下图所示:
HDFS上的显示结果:
2.2 INSERT插入数据
语法结构:
(1)插入一条数据(一般不使用)
INSERT INTO TABLE table_name VALUES(XX,YY,ZZ);
(2)利用查询语句将结果导入新表
INSERT OVERWRITE [INTO] TABLE table_name [PARTITION (partcol1=val1, partcol2=val2 ...)]
Select * FROM from_statement
(3)多重插入
FROM from_statement
INSERT OVERWRITE TABLE table_name1 [PARTITION (partcol1=val1, partcol2=val2 ...)]
select_statement1
INSERT OVERWRITE TABLE table_name2 [PARTITION (partcol1=val1, partcol2=val2 ...)]
select_statement2]
...
多重插入示例
FROM student3
INSERT INTO TABLE student3_1 SELECT id,name
INSERT INTO TABLE student3_2 SELECT age;
运行结果如下图所示:
查询表中结果:
- CTAS(create table … as select …)
在实际情况中,表的输出结果可能太多,不适于显示在控制台上,这时候,将 Hive 的查询输出结果直接存在一个新的表中是非常方便的,我们称这种情况为 CTAS。
具体示例:
CREATE TABLE student3_3 AS SELECT id,name,age FROM student3;
运行结果如下图所示:
查询表中结果:
注意: CTAS 操作是原子的,因此如果 select 查询由于某种原因而失败,新表是不会创建的!
2.3 INSERT导出数据
语法结构:
(1)单模式导出
INSERT OVERWRITE(不可省略) [LOCAL] DIRECTORY directory1 select_statement
(2)多模式导出
FROM from_statement
INSERT OVERWRITE [LOCAL] DIRECTORY directory1 select_statement1
[INSERT OVERWRITE [LOCAL] DIRECTORY directory2 select_statement2]
...
具体示例:
(1)导出数据到本地
INSERT OVERWRITE LOCAL DIRECTORY '/home/shiny/Desktop/test/students.txt'
SELECT * FROM student3;
运行结果如下图所示:
查询文件内容:
注意: 数据写入到文件系统时进行文本序列化,且每列用^A 来区分, \n 为换行符。
(2)导出数据到HDFS
INSERT OVERWRITE DIRECTORY '/students'
SELECT * FROM student3;
运行结果如下图所示:
查询文件内容:
2.4 SELECT查询数据
语法结构:
SELECT [ALL | DISTINCT] select_ condition, select_ condition, ...
FROM table_name a
[JOIN table_other b ON a.id = b.id]
[WHERE where_condition]
[GROUP BY col_list [HAVING condition]]
[CLUSTER BY col_list | [DISTRIBUTE BY col_list] [SORT BY col_list | ORDER BY col_list] ]
[LIMIT number]
建表语句解释:
(1)select_ condition: 查询字段
(2)table_name: 表名
(3)ORDER BY: 会对输入做全局排序,因此只有一个 reducer,只有一个 reduce task 的结果,比如文件名是 000000_0,会导致当输入规模较大时,需要较长的计算时间。
(4)SORT BY: 不是全局排序,其在数据进入 reducer 前完成排序。因此,如果用 SORT BY 进行排序,并且设置 mapreduce.job.reduces > 1,则 SORT BY 只保证每个 reducer 的输出有序,不保证全局有序。
那万一,我要对我的所有处理结果进行一个综合排序,而且数据量又非常大,那么怎么解决?
我们不适用 ORDER BY 进行全数据排序,我们适用 SORT BY 对数据进行局部排序,完了之后,再对所有的局部排序结果做一个归并排序。
- DISTRIBUTE BY(字段):根据指定的字段将数据分到不同的 reducer,且分发算法是 hash 散列。
(6)CLUSTER BY(字段): 除了具有 DISTRIBUTE BY 的功能外,还会对该字段进行排序。
因此,如果分桶和 SORT 字段是同一个时,此时, CLUSTER BY = DISTRIBUTE BY + SORT BY
如果我们要分桶的字段和要排序的字段不一样,那么我们就不能适用 CLUSTER BY。
分桶表的作用:最大的作用是用来提高 Join 操作的效率。
具体示例:
(1)GROUP BY:按学生姓名汇总学生年龄
SELECT name,SUM(age) FROM student_buck GROUP BY name;
student_buck表中的数据为:
运行结果如下图所示:
(2)ORDER BY: 获取班级年龄最大的五个学生
SELECT * FROM student3 ORDER BY age DESC LIMIT 5;
运行结果如下图所示:
(3)SORT BY: 查询学生信息,按照id降序排序
SET mapreduce.job.reduces=2; ##设置reduce的个数为2
SELECT * FROM STUDENT3 SORT BY age DESC;
运行结果如下图所示:
(4)DISTRIBUTE BY + SORT BY: 分桶和排序的组合操作,对 id 进行分桶,对 age, id 进行降序排序(先对 age 进行降序排序,age 相同的情况下对 id 进行降序排序)
SET mapreduce.job.reduces; ##查看reduce的个数
INSERT OVERWRITE LOCAL DIRECTORY '/home/shiny/Desktop/test/students_buck'
SELECT * FROM student3 DISTRIBUTE BY id SORT BY age DESC,id DESC;
运行过程如下图所示:
运行结果如下图所示:【新版本的hive文件会进行合并】
(5)CLUSTER BY: 分桶和排序的组合操作,等于DISTRIBUTE BY + SORT BY。对 id 进行分桶,对 id 进行升序排序
INSERT OVERWRITE LOCAL DIRECTORY '/home/shiny/Desktop/test/students_buck'
SELECT * FROM student3 CLUSTER BY id ;
运行过程如下图所示:
运行结果如下图所示:
- 补充:
1. 设置 reducetask 数量,设置的数量是分桶的数量
SET mapreduce.job.reduces=2;
2. 设置分桶为true
SET hive.enforce.bucketing = true;
默认:false;设置为true之后,mr运⾏时会根据bucket的个数⾃动分配reduce task个数。(⽤户也可以⾃⼰设置reduce任务个数,但分桶时不推荐使⽤)
3.Hive高级操作
1、Hive的Join操作
1.1 语法结构
join_table:
table_reference JOIN table_factor [join_condition]
| table_reference {LEFT|RIGHT|FULL} [OUTER] JOIN table_reference join_condition
| table_reference LEFT SEMI JOIN table_reference join_condition
Hive 支持等值连接(equality join)、外连接(outer join)和 左半连接(left semi join)。 Hive 不支持非等值的连接,因为非等值连接非常难转化到 Map/Reduce 任务。
另外, Hive 支持多于 2 个表的连接。
写查询时要注意以下几点:
只支持等值链接
例如:
SELECT a.* FROM a JOIN b ON (a.id = b.id);
SELECT a.* FROM a JOIN b ON (a.id = b.id AND a.department = b.department);
是正确的;
SELECT a.* FROM a JOIN b ON (a.id > b.id);
是错误的。
可以 Join 多于 2 个表
例如:
SELECT a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key2);
如果 Join 中多个表的 join key 是同一个,则 Join 会被转化为单个 Map/Reduce 任务。
例如:
SELECT a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key1);
被转化为单个 Map/Reduce 任务,因为 Join 中只使用了 b.key1 作为 join key。
例如:
SELECT a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key2);
而这一 Join 被转化为 2 个 Map/Reduce 任务。因为 b.key1 用于第一次 Join 条件,而 b.key2 用于第二次 Join。
Join 时,每次 Map/Reduce 任务的逻辑
Reducer 会缓存 Join 序列中除了最后一个表的所有表的记录,再通过最后一个表将结果序列化到文件系统。这一实现有助于在 Reduce 端减少内存的使用量。实践中,应该把最大的那个表写在最后(否则会因为缓存浪费大量内存)。
例如:
SELECT a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key1);
所有表都使用同一个 join key(使用 1 次 Map/Reduce 任务计算)。 Reduce 端会缓存 a 表和 b 表的记录,然后每次取得一个 c 表的记录就计算一次 Join 结果。
类似的还有:
SELECT a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key2);
这里用了 2 次 Map/Reduce 任务。第一次缓存 a 表,用 b 表序列化;第二次缓存第一次 Map/Reduce 任务的结果,然后用 c 表序列化。
1.2 数据准备
创建两张表
CREATE TABLE studenta(
id INT,
name STRING)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ',';
CREATE TABLE studentb(
id INT,
age INT)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ',';
准备数据
[shiny@hadoop01 test]$ cat studenta.txt
10001 shiny
10002 mark
10003 angel
10005 ella
10009 jack
10014 eva
10018 judy
10020 cendy
[shiny@hadoop01 test]$ cat studentb.txt
10001 23
10004 22
10007 24
10008 21
10009 25
10012 25
10015 20
10018 19
10020 26
导入数据
LOAD DATA LOCAL INPATH '/home/shiny/Desktop/test/studenta.txt'
INTO TABLE studenta;
LOAD DATA LOCAL INPATH '/home/shiny/Desktop/test/studentb.txt'
INTO TABLE studentb;
-Join详解
Hive Join 分三种: INNER JOIN, OUTER JOIN, SEMI JOIN。
其中:OUTER JOIN 包括 LEFT JOIN、RIGHT JOIN 和 FULL OUTER JOIN,主要用来处理 Join 中空记录的情况。
1. JOIN(内连接,等同于INNER JOIN)
--作用:把符合两边连接条件的数据查询出来
SELECT * FROM studenta a JOIN studentb b
ON a.id=b.id;
运行结果如下图所示:
LEFT JOIN(左外连接,等同于LEFT OUTER JOIN)
作用:
(1)以左表数据为匹配标准,左大右小;
(2)匹配不上的就是 null;
(3)返回的数据条数与左表相同。
SELECT * FROM studenta a LEFT JOIN studentb b
ON a.id=b.id;
运行结果如下图所示:
RIGHT JOIN(右外连接,等同于RIGHT OUTER JOIN)
作用:
(1)以右表数据为匹配标准,左小右大;
(2)匹配不上的就是 null;
(3)返回的数据条数与右表相同。。
SELECT * FROM studenta a RIGHT JOIN studentb b
ON a.id=b.id;
运行结果如下图所示:
FULL JOIN(全外连接,等同于FULL OUTER JOIN)
作用:
(1)以两个表的数据为匹配标准;
(2)匹配不上的就是 null;
(3)返回的数据条数等于两表数据去重之和。
SELECT * FROM studenta a FULL JOIN studentb b
ON a.id=b.id;
运行结果如下图所示:
LEFT SEMI JOIN(左半连接)
作用:因为 Hive 不支持 in/exists 操作(1.2.1 版本的 Hive 支持 in 操作),所以用该操作实现,并且是 in/exists 的高效实现。
(1)把符合两边连接条件的左表的数据显示出来。
(2)右表只能在 ON 子句中设置过滤条件,在 WHERE 子句、SELECT 子句或其他地方过滤都不行。
SELECT * FROM studenta a LEFT SEMI JOIN studentb b
ON a.id=b.id;
运行结果如下图所示:
2、Hive的数据类型
Hive支持两种数据类型,一类叫原子数据类型,一类叫复杂数据类型。
2.1 原子数据类型
原子数据类型包括数值类型、布尔类型、字符串类型、日期时间类型,具体如下表所示:
- Hive 是用 Java 开发的, Hive 里的基本数据类型和 Java 的基本数据类型也是一一对应的,虽然受到一些MySQL命名的影响。
(2)有符号的整数类型: TINYINT、SMALLINT、INT 和 BIGINT 分别等价于 Java 的 Byte、Short、Int 和 Long 原子类型,它们分别为 1 字节、2 字节、4 字节和 8 字节有符号整数。
(3)Hive 的浮点数据类型 FLOAT 和 DOUBLE,对应于 Java 的基本类型 Float 和 Double 类型,它们分别为 32 位和 64 位浮点数。
(4)Hive 的 BOOLEAN 类型相当于 Java 的基本数据类型 Boolean。
(5)Hive 的 String 类型相当于数据库的 Varchar 类型,该类型是一个可变的字符串,不过它不能声明其中最多能存储多少个字符,理论上它可以存储 2GB 的字符数。
(6)Hive 的 VARCHAR与STRING类似,但是长度上只允许在1-65355之间,它对应数据库的Varchar 类型。
(7)Hive 的 CHAR 则用固定长度来存储数据,CHAR类型的最大长度为255,它对应数据库的Char 类型。
(8)Hive中的TIMESTAMP与时区无关,存储为UNIX纪元的偏移量。
(9) Hive中DATE类型的值描述了特定的年月日,以YYYY- MM- DD格式表示,例如2015-05-29。DATE类型不包含时间,所表示日期的范围为0000- 01- 01 to 9999- 12- 31。DATE类型仅可与DATE、TIMESTAMP、STRING类型相互转化。
2.2 复杂数据类型
复杂数据类型包括数组(ARRAY)、映射(MAP)和结构体(STRUCT),具体如下所示:
数据类型说明:
- ARRAY: ARRAY 类型是由一系列相同数据类型的元素组成,这些元素可以通过下标来访问。比如有一个 ARRAY 类型的变量 fruits,它是由['apple','orange','mango']组成,那么我们可以通过 fruits[1]来访问元素 orange,因为 ARRAY 类型的下标是从 0 开始的;
(2)MAP: MAP 包含 key->value 键值对,可以通过 key 来访问元素。比如” userlist”是一个 map类型,其中 username 是 key, password 是 value;那么我们可以通过 userlist['username']
来得到这个用户对应的 password;
(3)STRUCT: STRUCT 可以包含不同数据类型的元素。这些元素可以通过”点语法”的方式来得到所需要的元素,比如 user 是一个 STRUCT 类型,那么可以通过 user.address 得到这个用户的地址。
示例:
CREATE TABLE student(
name STRING,
favors ARRAY<STRING>,
scores MAP<STRING, FLOAT>,
address STRUCT<province:STRING,city:STRING,detail:STRING,zip:INT>
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t'
COLLECTION ITEMS TERMINATED BY ';' 元素分隔符(Array 中的各元素、 Struct 中的各元素、Map 中的 key/value 对之间),
MAP KEYS TERMINATED BY ':' ;
示例说明:
(1)字段 name 是基本类型, favors 是数组类型,可以保存很多爱好, scores 是映射类型,可以保存多个课程的成绩, address 是结构类型,可以存储住址信息。
(2)ROW FORMAT DELIMITED 是指明后面的关键词是列和元素分隔符的。
(3)FIELDS TERMINATED BY 是字段分隔符。
(4)COLLECTION ITEMS TERMINATED BY 是元素分隔符(Array 中的各元素、 Struct 中的各元素、Map 中的 key/value 对之间),
(5)MAP KEYS TERMINATED BY 是 Map 中 key 与 value 的分隔符。
总结: 在关系型数据库中,我们至少需要三张表来定义,包括学生基本表、爱好表、成绩表;但在 Hive 中通过一张表就可以搞定了。也就是说,复合数据类型把多表关系通过一张表就可以实现了。
2.2.1 ARRAY
建表语句
CREATE TABLE employee(
name STRING,
age INT,
work_locations ARRAY<STRING>)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t'
COLLECTION ITEMS TERMINATED BY ','; ##ARRAY 中的各元素的分隔符
数据准备
[shiny@hadoop01 test]$ cat array.txt
shiny 23 beijing,tianjin,qingdao
jack 34 shanghai,guangzhou
mark 26 beijing,xian
ella 21 beijing
judy 30 shanghai,hangzhou,chongqing
cendy 28 beijing,shanghai,dalian,chengdu
导入数据
LOAD DATA LOCAL INPATH '/home/shiny/Desktop/test/array.txt'
INTO TABLE employee;
查询语句
SELECT * FROM employee;
SELECT work_locations FROM employee;
SELECT work_locations[0] FROM employee;
运行结果如下图所示:
2.2.2 MAP
建表语句
CREATE TABLE score(
name STRING,
scores MAP<STRING,INT>)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t'
COLLECTION ITEMS TERMINATED BY ',' ##MAP 中的 key/value 对之间的分隔符
MAP KEYS TERMINATED BY ':'; ##MAP 中 key 与 value 的分隔符
数据准备
[shiny@hadoop01 test]$ cat scores.txt
shiny chinese:90,math:100,english:99
mark chinese:89,math:56,english:87
judy chinese:94,math:78,english:81
ella chinese:54,math:23,english:48
jack chinese:100,math:95,english:69
cendy chinese:67,math:83,english:45
导入数据
LOAD DATA LOCAL INPATH '/home/shiny/Desktop/test/scores.txt'
INTO TABLE score;
查询语句
SELECT * FROM score;
SELECT scores FROM score;
SELECT scores['math'] FROM score;
运行结果如下图所示:
2.2.3 STRUCT
建表语句
CREATE TABLE coursescore(
id INT,
course STRUCT<name:STRING,score:INT>)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t'
COLLECTION ITEMS TERMINATED BY ','; ##STRUCT 中的各元素的分隔符
数据准备
[shiny@hadoop01 test]$ cat coursescore.txt
1 chinese,100
2 math,98
3 english,99
4 computer,78
导入数据
LOAD DATA LOCAL INPATH '/home/shiny/Desktop/test/coursescore.txt'
INTO TABLE coursescore;
查询语句
SELECT * FROM coursescore;
SELECT course FROM coursescore;
SELECT course.name FROM coursescore;
SELECT course.score FROM coursescore;
运行结果如下图所示:
3、Hive函数
3.1 Hive内置函数
3.1.1 初识Hive内置函数
内容较多,见《Hive 官方文档》
LanguageManual UDF - Apache Hive - Apache Software Foundation
查看内置函数
SHOW FUNCTIONS;
运行结果如下图所示:
显示函数的详细信息
DESC FUNCTION trim;
运行结果如下图所示:
显示函数的扩展信息
DESC FUNCTION EXTENDED trim;
测试内置函数的快捷方式:
(1)创建一个 student 表 CREATE TABLE student(id STRING);
(2)LOAD 一个文件(一行,一个空格)到 student 表
(3)SELECT substr('shiny',2,3) FROM student;
3.1.2 需求:Json 数据解析
现有原始 Json 数据(rating.json)如下所示:
{"movie":"1193","rate":"5","timeStamp":"978300760","uid":"1"}
{"movie":"661","rate":"3","timeStamp":"978302109","uid":"1"}
{"movie":"914","rate":"3","timeStamp":"978301968","uid":"1"}
{"movie":"3408","rate":"4","timeStamp":"978300275","uid":"1"}
{"movie":"2355","rate":"5","timeStamp":"978824291","uid":"1"}
{"movie":"1197","rate":"3","timeStamp":"978302268","uid":"1"}
{"movie":"1287","rate":"5","timeStamp":"978302039","uid":"1"}
{"movie":"2804","rate":"5","timeStamp":"978300719","uid":"1"}
{"movie":"594","rate":"4","timeStamp":"978302268","uid":"1"}
现在需要将数据导入到 Hive 仓库中,并且最终要得到这么一个结果:
可用内置函数 get_json_object 或者自定义函数完成
3.1.3 内置函数:get_json_object实现
准备Json数据:
{"movie":"1193","rate":"5","timeStamp":"978300760","uid":"1"}
{"movie":"661","rate":"3","timeStamp":"978302109","uid":"1"}
{"movie":"914","rate":"3","timeStamp":"978301968","uid":"1"}
先加载 rating.json 文件到 Hive 的一个原始表 rat_json中。
创建rat_json表
CREATE TABLE rat_json(line STRING)
ROW FORMAT DELIMITED;
加载数据
LOAD DATA LOCAL INPATH '/home/shiny/Desktop/test/rating.json'
INTO TABLE rat_json;
需要解析Json数据成四个字段,插入一张新的表 rate(用于存放处理的数据,需要有四个字段)。
CREATE TABLE rate(
movie INT,
rate INT,
ts INT,
userid INT)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t';
解析Json,得到结果之后存入rate表。
INSERT INTO TABLE rate
SELECT
get_json_object(line,'$.movie') AS movie,
get_json_object(line,'$.rate') AS rate,
get_json_object(line,'$.timeStamp') AS ts,
get_json_object(line,'$.uid') AS userid
FROM rat_json;
查询结果,解析成功
3.2 Hive自定义函数
当 Hive 提供的内置函数无法满足业务处理需要时,此时就可以考虑使用用户自定义函数。
(1)UDF(user-defined function):用户自定义函数作用于单个数据行,产生一个数据行作为输出。(数学函数,字符串函数)
(2)UDAF(User- Defined Aggregation Funcation):用户自定义聚合函数接收多个输入数据行,并产生一个输出数据行。(COUNT、MAX)
3.2.1 UDF示例:解析Json数据
新建一个项目HiveDemo,导入apache-hive-1.2.1-bin\lib下的所有包;
开发一个简单的 Java 类,继承org.apache.hadoop.hive.ql.exec.UDF,重载 evaluate 方法。
package com.shiny.hive.udf;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.json.JSONException;
import org.json.JSONObject;
//解析Json格式数据
public class JsonUDF extends UDF{
//必须是public
public String evaluate(String jsonStr,String field) throws JSONException{
JSONObject json=new JSONObject(jsonStr);
String result=(String) json.get(field);
return result;
}
}
3. 打成json.jar包上传到Linux服务器
4. 将jar包添加到Hive的classpath下
hive> ADD JAR /home/shiny/Desktop/test/json.jar;
hive> LIST JAR; ##查看jar包是否加入成功
运行结果如下图所示:
创建临时函数与开发好的 class 关联起来
hive> CREATE TEMPORARY FUNCTION jsontostring AS 'com.shiny.hive.udf.JsonUDF';
sontostring :临时函数的名字
com.shiny.hive.udf.JsonUDF:包名.类名
解析Json,得到结果之后存入rates表(rates表用于存放处理的数据,所以需要有四个字段)。
CREATE TABLE rates
AS SELECT
jsontostring(line,'movie') AS movie,
jsontostring(line,'rate') AS rate,
jsontostring(line,'timeStamp') AS ts,
jsontostring(line,'uid') AS userid
FROM rat_json;
至此,便可以使用自定义的函数
SELECT * FROM rates LIMIT 5;
运行结果如下图所示:
4、Hive Shell操作
以下表格中是Hive支持的一些命令:
语法结构:
hive [-hiveconf x=y] [<-i filename>] [<-f filename>|<-e query-string>] [-S]
说明:
(1)-i:从文件初始化 HQL。
(2)-e:从命令行执行指定的 HQL
(3)-f:执行 HQL 脚本
(4)-v:输出执行的 HQL 语句到控制台
(5)-p: connect to Hive Server on port number
(6)-hiveconf x=y(Use this to set hive/hadoop configuration variables)
(7)-S:表示以不打印日志的形式执行命名操作
示例:
(1)运行一个查询
[shiny@hadoop01 ~]$ hive -e 'SELECT * FROM shiny.student3'
运行结果如下图所示
- 运行一个文件
- 运行参数文件
从配置文件启动 Hive,并加载配置文件当中的配置参数。