由图我们可以得知:
用户连接接口
CLI:是指Shell命令行
JDBC/ODBC:是指Hive的java实现,与传统数据库JDBC类似。
WebUI:是指可通过浏览器访问Hive。
元数据:Metastore
元数据包括:表名、表所属的数据库(默认是default)、表的拥有者、列/分区字段、表的类型(是否是外部表)、表的数据所在目录等;
默认存储在自带的derby数据库中,推荐使用MySQL存储Metastore
thriftserver:
hive的可选组件,此组件是一个软件框架服务,允许客户端使用包括Java、C++、Ruby和其他很多种语言,通过编程的方式远程访问Hive。
驱动器(Driver)
hadoop
Jobtracker是hadoop1.x中的组件,它的功能相当于:Resourcemanager+AppMaster
TaskTracker相当于:Nodemanager + yarnchild
Hive的数据存储在HDFS中,大部分的查询、计算由MapReduce完成
hive与hadoop关系:
hive与传统数据库比较:
hive与mysql比较:
解压hive
创建文件夹
cd hive
mkdir warehouse
配置环境变量
vim /etc/profile
#hive
export HIVE_HOME=/usr/local/study/hive
export PATH=$HIVE_HOME/bin:$PATH
source /etc/profile
安装mysql
解压
mkdir mysql
# tar -xvf mysql-8.0.29-1.el7.x86_64.rpm-bundle.tar -C mysql
#安装
cd mysql
rpm -ivh mysql-community-common-8.0.29-1.el7.x86_64.rpm --nodeps --force
rpm -ivh mysql-community-libs-8.0.29-1.el7.x86_64.rpm --nodeps --force
rpm -ivh mysql-community-client-8.0.29-1.el7.x86_64.rpm --nodeps --force
rpm -ivh mysql-community-server-8.0.29-1.el7.x86_64.rpm --nodeps --force
对mysql初始化配置
mysqld --initialize
chown mysql:mysql /var/lib/mysql -R
#启动MySQL服务
systemctl start mysqld.service
systemctl enable mysqld
#改密码
cat /var/log/mysqld.log | grep password
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'Root123.';
#改权限
use mysql;
select Host ,User from user;
update user set Host='%' where User='root';
新建元数据库
mysql -uroot -pRoot123.
create database metastore;
exit;
配置属性
修改 hive-env.sh
HADOOP_HOME=/usr/local/study/hadoop
export HIVE_CONF_DIR=/usr/local/study/hive/conf
export HIVE_AUX_JARS_PATH=/usr/local/study/hive/lib
创建 hive-site.xml
vim conf/hive-site.xml
<configuration>
<property>
<name>hive.metastore.warehouse.dirname>
<value>/usr/local/study/hive/warehousevalue>
property>
<property>
<name>hive.metastore.localname>
<value>truevalue>
property>
<property>
<name>javax.jdo.option.ConnectionURLname>
<value>jdbc:mysql://127.0.0.1:3306/metastore?createDatabaseIfNotExist=truevalue>
property>
<property>
<name>javax.jdo.option.ConnectionDriverNamename>
<value>com.mysql.cj.jdbc.Drivervalue>
property>
<property>
<name>javax.jdo.option.ConnectionUserNamename>
<value>rootvalue>
property>
<property>
<name>javax.jdo.option.ConnectionPasswordname>
<value>Root123.value>
property>
configuration>
放置mysql驱动包
cp mysql-connector-java-8.0.29.jar /usr/local/study/soft
scp到另俩个linux中
scp -r hive node2:$PWD
scp -r hive node3:$PWD
另外节点配置环境变量和mysql
vim /etc/profile
#hive
export HIVE_HOME=/usr/local/study/hive
export PATH=$HIVE_HOME/bin:$PATH
source /etc/profile
另外俩个节点配置mysql
scp -r mysql-8.0.29-1.el7.x86_64.rpm-bundle.tar node2:$PWD
scp -r mysql-8.0.29-1.el7.x86_64.rpm-bundle.tar node3:$PWD
解压
mkdir mysql
tar -xvf mysql-8.0.29-1.el7.x86_64.rpm-bundle.tar -C ../mysql
#安装
cd mysql
rpm -ivh mysql-community-common-8.0.29-1.el7.x86_64.rpm --nodeps --force
rpm -ivh mysql-community-libs-8.0.29-1.el7.x86_64.rpm --nodeps --force
rpm -ivh mysql-community-client-8.0.29-1.el7.x86_64.rpm --nodeps --force
rpm -ivh mysql-community-server-8.0.29-1.el7.x86_64.rpm --nodeps --force
#对mysql初始化配置
mysqld --initialize
chown mysql:mysql /var/lib/mysql -R
#启动MySQL服务
systemctl start mysqld.service
systemctl enable mysqld
#改密码
cat /var/log/mysqld.log | grep password
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'Root123.';
#改权限
use mysql;
select Host ,User from user;
update user set Host='%' where User='root';
master启动Hadoop
start-all.sh
zkServer.sh
三台都启动zookeeper
zkServer.sh
启动MySQL
service mysql start
初始化元数据
schematool -initSchema -dbType mysql -verbose
在2个slave节点中hive-site.xml添加
测试启动
hive
Hive数据类型 | Java数据类型 | 长度 | 例子 |
---|---|---|---|
TINYINT | byte | 1byte有符号整数 | 20 |
SMALINT | short | 2byte有符号整数 | 20 |
INT |
int | 4byte有符号整数 | 20 |
BIGINT | long | 8byte有符号整数 | 20 |
BOOLEAN | boolean | 布尔类型,true或者false | TRUE FALSE |
FLOAT | float | 单精度浮点数 | 3.14159 |
DOUBLE |
double | 双精度浮点数 | 3.14159 |
STRING |
string | 字符系列。可以指定字符集。可以使用单引号或者双引号。 | ‘now is the time’ “for all good men” |
TIMESTAMP | 时间类型 | '2013-01-31 00:13:00.345’ | |
BINARY | 字节数组(二进制) | 1010 |
红标为常用的数据类型;
对于Hive的String类型相当于数据库的varchar类型,该类型是一个可变的字符串,不过它不能声明其中最多能存储多少个字符。
数据类型 | 描述 | 语法示例 |
---|---|---|
STRUCT | 相当于java语言当中没有方法的对象,只有属性。例如,如果某个列的数据类型是STRUCT{first STRING, last STRING},那么第1个元素可以通过字段.first 来引用。 |
struct() |
MAP | MAP是一组键-值对元组集合,使用数组表示法可以访问数据。例如,如果某个列的数据类型是MAP,其中键->值对是’first’->’John’和’last’->’Doe’,那么可以通过字段名[‘last’] 获取最后一个元素 |
map() |
ARRAY | 数组是一组具有相同类型和名称的变量的集合。这些变量称为数组的元素,每个数组元素都有一个编号,编号从零开始。例如,数组值为[‘John’, ‘Doe’],那么第2个元素可以通过数组名[1] 进行引用。 |
Array() |
可以使用CAST操作显示进行数据类型转换
例如CAST(‘1’ AS INT)将把字符串’1’ 转换成整数1;如果强制类型转换失败,如执行CAST(‘X’ AS INT),表达式返回空值 NULL。
库操作:
-- 查看数据库
show databases;
show databases like 'hivetest*';
-- 切换数据库
use mydb;
-- 创建数据库
create database zoo;
create database if not exists zoo;
create database if not exists qfdb comment '....';
create database if not exists hivetest location 'hdfs路径';
-- 查看数据库信息
语法1:desc database databaseName;
语法2:desc database extended databaseName;
语法3:describe database extended databaseName;
-- 删除数据库
语法1:drop database databasename;#删除空库
drop database if exists databasename;
语法2:drop database databasename cascade;# 强制删除
表操作:
查看数据库中表:
show tables;
# 查看另外一个数据库中的表
show tables in zoo;
查看表结构:
desc tableName
desc extended tableName;
describe extended tableName;
创建表:
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]
CREATE TABLE 创建一个指定名字的表。如果相同名字的表已经存在,则抛出异常;用户可以用 IF NOT EXISTS 选项来忽略这个异常。
EXTERNAL关键字可以让用户创建一个外部表,在建表的同时指定一个指向实际数据的路径(LOCATION),Hive创建内部表时,会将数据移动到数据仓库指向的路径
;若创建外部表,仅记录数据所在的路径,不对数据的位置做任何改变。在删除表的时候,内部表的元数据和数据会被一起删除,而外部表只删除元数据,不删除数据。
COMMENT:为表和列添加注释。
PARTITIONED BY创建分区表
CLUSTERED BY创建分桶表
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,property_name=property_value, …)]
用户在建表的时候可以自定义SerDe或者使用自带的SerDe。如果没有指定ROW FORMAT 或者ROW FORMAT DELIMITED,将会使用自带的SerDe。在建表的时候,用户还需要为表指定列,用户在指定表的列的同时也会指定自定义的SerDe,Hive通过SerDe确定表的具体的列的数据。
SerDe是Serialize/Deserilize的简称,目的是用于序列化和反序列化。
STORED AS指定存储文件类型
常用的存储文件类型:SEQUENCEFILE(二进制序列文件)、TEXTFILE(文本)、RCFILE(列式存储格式文件)
LOCATION :指定表在HDFS上的存储位置。
LIKE:允许用户复制现有的表结构,但是不复制数据。
例子:
语法1:
create table t_user(id int,name string);
语法2:使用库.表的形式
create table mydb.t_user(id int,name string);
语法3:指定分隔规则形式
create table if not exists t1(
uname string comment 'this is name',
chinese int,
math int,
english int
)
comment 'this is my table'
row format delimited
fields terminated by '\t'
lines terminated by '\n'
stored as textfile;
修改表结构
- 修改表名
alter table oldTableName rename to newTableName;
- 修改列名: 和修改字段类型是同一个语法
alter table tableName change column oldName newName colType;
alter table tableName change column colName colName colType;
- 修改列的位置: 注意,2.x版本后,必须是相同类型进行移动位置。
alter table tableName change column colName colName colType after colName1;
alter table tableName change column colName colName colType first;
- 增加字段:
alter table tableName add columns (sex int,...);
- 删除字段: #注意,2.x版本后,注意类型的问题,替换操作,其实涉及到位置的移动问题。
alter table tableName replace columns(
id int,
name int,
size int,
pic string
);
注意:实际上是保留小括号内的字段。
删除表:
drop table tableName;
内部表
{hive.metastore.warehouse.dir}
下的相应的库对应的目录中。当我们删除一个管理表时,Hive也会删除这个表中数据。
管理表不适合和其他工具共享数据。
创建内部表
-- 普通创建表
create table if not exists student2(
id int,
name string
)
row format delimited fields terminated by '\t';
-- 根据查询结果创建表(查询的结果会添加到新创建的表中)
create table if not exists student3 as select id, name from student;
-- 根据已经存在的表结构创建表
create table if not exists student4 like student;
-- 查询表的类型
desc formatted student2;
外部表
删除该表并不会删除掉这份数据,不过描述表的元数据信息会被删除掉。
create external table if not exists student2(
id int,
name string
)
row format delimited
fields terminated by '|'
collection items terminated by ','
map keys terminated by ':'
lines terminated by '\n';
内部表与外部表转换:
-- 内转外
alter table student2 set tblproperties('EXTERNAL'='TRUE');
-- 外转内
alter table student2 set tblproperties('EXTERNAL'='FALSE');
两者应用场景:
每天将收集到的网站日志定期流入HDFS文本文件。在外部表(原始日志表)的基础上做大量的统计分析,用到的中间表、结果表使用内部表存储,数据通过SELECT+INSERT进入内部表。
加载数据
load
load data [local] inpath '路径' [overwrite] into table 表名 [partition (partcol1=val1,…)];
insert
基本插入
insert into table student partition(month='201709') values(1,'wangwu');
insert overwrite table student partition(month='201708') select id, name from student where month='201709';
多插入
from dept_partition
insert overwrite table dept_partition partition(month='201707')
select deptno,dname,loc where month='201709'
insert overwrite table dept_partition partition(month='201706')
select deptno,dname,loc where month='201709';
as select( 查询语句中创建表并加载数据)
create table if not exists student3 as select id, name from student;
location (创建表时通过Location指定加载数据路径)
create table if not exists student5(
id int,
name string
)
row format delimited fields terminated by '\t'
location '/user/hive/warehouse/student5';#hdfs路径
import
import table student2 partition(month='201709') from '/opt/hive/warehouse/export/student';
导入eport导出的数据
导出数据
insert
导出到本地
insert overwrite local directory '/opt/datas/dept1'
[row format delimited fields terminated by '|'] # 格式化后导出
select * from dept_partition;
导出到hdfs
insert local directory '/opt/datas/dept1'
row format delimited
fields terminated by '|'
select * from dept_partition;
hadoop命令导出
dfs -get /opt/hive/warehouse/employee/employee.txt /opt/datas/dept2/dept.txt;
hive shell导出
hive -e 'select * from hivetest.dept_partition;' > /opt/datas/dept3/dept.txt;
export导出
export table hivetest.dept_partition to '/opt/datas/dept2';
sqoop导出
清除表数据
truncate table student;
查询语法:
[WITH CommonTableExpression (, CommonTableExpression)*] (Note: Only available
starting with Hive 0.13.0)
SELECT [ALL | DISTINCT] select_expr, select_expr, ...
FROM table_reference
[WHERE where_condition]
[GROUP BY col_list]
[ORDER BY col_list]
[CLUSTER BY col_list
| [DISTRIBUTE BY col_list] [SORT BY col_list]
]
[LIMIT number]
select ..
from ..
join [tableName] on ..
where ..
group by ..
having ..
order by ..
sort by ..
limit ..
union | union all ...
普通查询和数据库SQL一样
执行顺序:
第一步: FROM
第二步: ON
第三步:
第四步: WHERE
第五步: GROUP BY
第六步: HAVING
第七步: SELECT
第八步: DISTINCT
第九步: ORDER BY
第十步: LIMIT
标准hive语句的一些规则:
查询原则
不同点:
**rlike:**通过Java的正则表达式
这个更强大的语言来指定匹配条件。
select * from emp where sal RLIKE '[2]';
Hive支持通常的SQL JOIN语句,但是只支持等值连接,不支持非等值连接。
**left semi join:**在hive中,有一种专有的join操作,left semi join,我们称之为半开连接。它是left join的一种优化形式,只能查询左表的信息,主要用于解决hive中左表的数据是否存在的问题。相当于exists关键字的用法。hive中不支持right semi join
-- 左外连接,左表中的数据全部返回
select * from u1 left join u2 on u1.id =u2.id;
select * from u1 left outer join u2 on u1.id =u2.id;
-- 左半开连接,只显示左表中满足条件的数据。和exists的逻辑是相同的
select * from u1 left semi join u2 on u1.id =u2.id;
-- exists的写法
select * from u1 where exists (select 1 from u2 where u2.id =u1.id);
-- 验证left semi join 是否可以显示第二张表的信息:错误写法。
select A.*, B.* from u1 A left semi join u2 B on A.id =B.id;
排序
**order by:**全局排序,一个Reducer
ASC(ascend): 升序(默认)
DESC(descend): 降序
select * from emp order by sal;
**Sort By:**每个Reducer内部进行排序,对全局结果集来说不是排序
1.设置reduce个数
set mapreduce.job.reduces=3;
2.查看设置reduce个数
set mapreduce.job.reduces;
3.根据部门编号降序查看员工信息
select *from dept_partition sort by deptno;
Distribute By:类似MR中partition,进行分区,结合sort by使用。
注意,Hive要求DISTRIBUTE BY语句要写在SORT BY语句之前。
select *from dept_partition distribute by deptno sort by month;
Cluster By:
当distribute by和sorts by字段相同时,可以使用cluster by方式。
cluster by除了具有distribute by的功能外还兼具sort by的功能。但是排序只能是升序排序
,不能指定排序规则为ASC或者DESC。
select *from dept_partition distribute by deptno sort by deptno;
select *from dept_partition cluster by deptno;
为什么分区?
Hive中存放的数据往往是以PB为单位的庞大的数据集,海量的数据需要耗费大量的时间去处理,若是每次查询都对全部数据进行检索,效率会极为低下。而且在计多场景下,我们并不需要对全部数据进行检索,因此引入分区和分桶的方法减少每一次扫描总数据量,这种做法可以显著地改善性能。
如何分区
分区的语法
create table tableName(
.......
)
partitioned by (colName colType [comment '...'],...)
分区的注意事项
分区的意义
分区的本质
建分区表
# 建表
create table if not exists part4(
id int,
name string
)
partitioned by (year string,month string,DAY string)
row format delimited fields terminated by ',';
-- 测试字段名的大小写,结果不区分。
#加载数据
load data local inpath './data/user1.txt' into table part4 partition(year='2018',month='03',DAy='21');
load data local inpath './data/user2.txt' into table part4 partition(year='2018',month='03',day='AA');
查看分区
show partitions tableName
修改分区:指的是修改分区字段值对应的映射位置。
alter table part3 partition(year='2020',month='05',day='01') set location 'hdfs://master:8020/user/hive/warehouse/mydb.db/part1/dt=2020-05-05';
增加分区
新增空分区
alter table part3 add partition(year='2020',month='05',day='02');
alter table part3 add partition(year='2020',month='05',day='03') partition(year='2020',month='05',day='04');
新增带数据分区
alter table part3 add partition(year='2020',month='05',day='05') location '/user/hive/warehouse/mydb.db/part1/dt=2020-05-06';
新增多分区
alter table part3 add
partition(year='2020',month='05',day='06') location '/user/hive/warehouse/mydb.db/part1/dt=2020-05-05'
partition(year='2020',month='05',day='07') location '/user/hive/warehouse/mydb.db/part1/dt=2020-05-06';
删除分区
删除单分区
alter table part3 drop partition(year='2020',month='05',day='07');
删除多个分区
alter table part3 drop
partition(year='2020',month='05',day='06'),
partition(year='2020',month='05',day='06');
分区的种类
分区的属性设置
hive.exec.dynamic.partition=true
,是否支持动态分区操作hive.exec.dynamic.partition.mode=strict/nonstrict
: 严格模式/非严格模式hive.exec.max.dynamic.partitions=1000
:总共允许创建的动态分区的最大数量hive.exec.max.dynamic.partitions.pernode=100
:in each mapper/reducer node各种分区的创建
动态分区
-- 建表
create table dy_part1(
...
)partitioned by (dt string) row format delimited fields terminated by ',';
-- 加载数据
#1.建临时表
create table temp_part1(
...
)row format delimited fields terminated by ',';
#2.导入数据到临时表
load data local inpath './data/student2.txt' into table temp_part1;
#3.动态加载到表
insert into dy_part1 partition(dt) select sid,name,gender,age,academy,dt from temp_part1;
注意:严格模式下,给动态分区表导入数据时,分区字段至少要有一个分区字段是静态值
非严格模式下,导入数据时,可以不指定静态值。
混合分区
-- 建表
create table dy_part2(
id int,
name string
)
partitioned by (year string,month string,day string)
row format delimited fields terminated by ','
;
--
#创建临时表
create table temp_part2(
id int,
name string,
year string,
month string,
day string
)
row format delimited fields terminated by ','
;
#导入数据
insert into dy_part2 partition (year='2020',month,day) select id,name,month,day from temp_part2;
分区表注意事项
Hive分区和Mysql分区的区别
分桶是相对分区进行更细粒度的划分。在分区数量过于庞大以至于可能导致文件系统崩溃时,我们就需要使用分桶来解决问题了。
分桶将整个数据内容按照某列属性值的Hash值进行区分。比如,如要按照ID属性分为4个桶,就是对ID属性值的Hash值对4取模,按照取模结果对数据分桶。例如,取模结果为0的数据记录存放到一个文件中,取模为1的数据存放到一个文件中,取模为2的数据存放到一个文件中。
分桶同样应当在建表时就建立,建表语句与建立分区表类似。我们还是创建表person,其建表语句如下:
CREATE TABLEperson(id INT,
name STRING,age INT,
fav ARRAY<STRING>,
addr MAP<STRING,STRING>)
COMMENT 'This is the person table'
ROW FORMAT DELIMITED FIELDS TERMINATED BY 'lt' PARTITIONED BY(dt STRING)
CLUSTERED BY (id)into 3 bucketsCOLLECTION ITEMS TERMINATED BY '-'MAP KEYS TERMINATED BY':'
STORED AS TEXTFILE;
以字段id为分桶依据,将每个分区分成3个桶,导入数据的语句与分区表时没有区别,还是向分区中导入数据。
LOAD DATA LOCAL INPATH 'person.txt' OVERWRITE INTO TABLE person partition(dt='20180315');
如果要查询3个桶中第1个桶中的全部数据,可以通过以下查询语句进行查询。
select * from person tablesample (bucket 1 out of 3 on id);
具体过程操作步骤如下(相同的步骤不再重复描述):
建表
向分区导入数据
查询,看分桶内是否成功。
select * from person tablesample(bucket 1 out of 3 on id)
在Hive中,函数主要分两大类型,一种是内置函数,一种是用户自定义函数。
查看函数
show functions;
desc function functionName;
日期函数
函数1:current_date()
;
当前系统日期 格式:“yyyy-MM-dd”
函数2:current_timestamp()
;
当前系统时间戳: 格式:“yyyy-MM-dd HH:mm:ss.ms”
函数3:unix_timestamp()
;
当前系统时间戳 格式:距离1970年1月1日0点的秒数。
函数3:unix_timestamp([date[,pattern]])
日期转时间戳函数
select unix_timestamp('1970-01-01 0:0:0');
函数4:datediff()、months_between()
计算时间差函数
select datediff('2019-11-20','2019-11-01');#返回天数
select months_between('2019-11-20','2019-11-01');#返回月份
函数5:year()、month()、day()、hour()、minute()、second()
日期时间分量函数
select year(current_date);
函数6:last_day()、next_day()
日期定位函数
--月末:
select last_day(current_date)
--下周
select next_day(current_date,'thursday');
函数7:date_add()、date_sub()、add_months()
日期加减函数
select date_add(current_date,1);
函数8:to_date()
字符安川转日期:yyyy-MM-dd格式
select to_date('2017-01-01 12:12:12');
函数8:date_format
日期转字符串(格式化)函数
select date_format(current_timestamp(),'yyyy-MM-dd HH:mm:ss');
select date_format(current_date(),'yyyyMMdd');
select date_format('2017-01-01','yyyy-MM-dd HH:mm:ss');
字符串函数
lower--(转小写)
select lower('ABC');
upper--(转大写)
select upper('abc');
length--(字符串长度,字符数)
select length('abc');
concat--(字符串拼接)
select concat("A", 'B');
concat_ws --(指定分隔符)
select concat_ws('-','a' ,'b','c');
substr--(求子串)
select substr('abcde',3);
split(str,regex)--切分字符串,返回数组。
select split("a-b-c-d-e-f","-");
类型转换函数
cast(value as type) -- 类型转换
select cast('123' as int)+1;
数学函数
round --四舍五入((42.3 =>42))
select round(42.3);
ceil --向上取整(42.3 =>43)
select ceil(42.3);
floor --向下取整(42.3 =>42)
select floor(42.3);
其他函数
nvl(value,default value)
-- 如果value为null,则使用default value,否则使用本身value.
isnull()
isnotnull()
case when then ....when ...then.. else... end
if(p1,p2,p3)
coalesce(col1,col2,col3...)返回第一个不为空的
窗口函数又名开窗函数,属于分析函数的一种。
是一种用于解决复杂报表统计需求的函数。
窗口函数常用于计算基于组的某种值,它和聚合函数的不同之处是:对于每个组返回多行,而聚合函数对于每个组只返回一行。
简单的说窗口函数对每条详细记录开一个窗口,进行聚合统计的查询
开窗函数指定了分析函数工作的数据窗口大小,这个数据窗口大小可能会随着行的变化而变化。
窗口函数一般不单独使用
窗口函数内也可以分组和排序
简单窗口函数:
格式:用函数+over()函数
例子:查询每个订单的信息,以及订单的总数
-- 不使用窗口函数
# 查询所有明细
select * from t_order;
# 查询总量
select count(*) from t_order;
-- 使用窗口函数
select *, count(*) over() from t_order;
窗口函数是针对每一行数据的.
如果over中没有指定参数,默认窗口大小为全部结果集
distribute by子句:在over窗口中进行分组,对某一字段进行分组统计,窗口大小就是同一个组的所有记录
语法:over(distribute by colname[,colname.....])
例子:查看顾客的购买明细及月购买总额
select name, orderdate, cost, sum(cost) over (distribute by month(orderdate)) from t_order;
saml 2018-01-01 10 205
saml 2018-01-08 55 205
tony 2018-01-07 50 205
saml 2018-01-05 46 205
sort by子句:sort by子句会让输入的数据强制排序 (强调:当使用排序时,窗口会在组内逐行变大)
语法:over([distribute by colname] [sort by colname [desc|asc]])
例子::查看顾客的购买明细及每个顾客的月购买总额,并且按照日期降序排序
select name, orderdate, cost,
sum(cost) over (distribute by name, month(orderdate) sort by orderdate desc)
from t_order;
注意:可以使用partition by + order by 组合来代替distribute by+sort by组合
select name, orderdate, cost,
sum(cost) over (partition by name, month(orderdate) order by orderdate desc)
from t_order;
例子:
注意:也可以在窗口函数中,只写排序,窗口大小是全表记录。
select name, orderdate, cost,
sum(cost) over (order by orderdate desc)
from t_order;
neil 2018-06-12 80 80 -统计信息会逐行增加
neil 2018-05-10 12 92
mart 2018-04-13 94 186
mart 2018-04-11 75 261
Window子句:如果要对窗口的结果做更细粒度的划分,那么就使用window子句
常见的:
PRECEDING:往前
FOLLOWING:往后
CURRENT ROW:当前行
UNBOUNDED:起点,
UNBOUNDED PRECEDING:表示从前面的起点,
UNBOUNDED FOLLOWING:表示到后面的终点
一般window子句都是rows开头
例子:
select name,orderdate,cost,
sum(cost) over() as sample1,--所有行相加
sum(cost) over(partition by name) as sample2,-- 按name分组,组内数据相加
sum(cost) over(partition by name order by orderdate) as sample3,-- 按name分组,组内数据累加
sum(cost) over(partition by name order by orderdate rows between UNBOUNDED PRECEDING and current row ) as sample4 ,-- 与sample3一样,由起点到当前行的聚合
sum(cost) over(partition by name order by orderdate rows between 1 PRECEDING and current row) as sample5, -- 当前行和前面一行做聚合
sum(cost) over(partition by name order by orderdate rows between 1 PRECEDING AND 1 FOLLOWING ) as sample6,-- 当前行和前边一行及后面一行
sum(cost) over(partition by name order by orderdate rows between current row and UNBOUNDED FOLLOWING ) as sample7 -- 当前行及后面所有行
from t_order;
NTILE:ntile 是Hive很强大的一个分析函数。可以看成是:它把有序的数据集合 平均分配 到 指定的数量(num)个桶中, 将桶号分配给每一行。如果不能平均分配,则优先分配较小编号的桶,并且各个桶中能放的行数最多相差1。
例子:
select name,orderdate,cost,
ntile(3) over(partition by name), # 按照name进行分组,在分组内将数据切成3份
from t_order;
mart 2018-04-13 94 1
mart 2018-04-11 75 1
mart 2018-04-09 68 2
mart 2018-04-08 62 3
neil 2018-06-12 80 1
neil 2018-05-10 12 2
saml 2018-01-01 10 1
LAG和LEAD函数:lag返回当前数据行的前第n行的数据,lead返回当前数据行的后第n行的数据
例子:求5分钟内点击100次的用户
dt id url
2019-08-22 19:00:01,1,www.baidu.com
2019-08-22 19:01:01,1,www.baidu.com
2019-08-22 19:02:01,1,www.baidu.com
2019-08-22 19:03:01,1,www.baidu.com
select id,dt,lag(dt,100) over(partition by id order by dt)
from tablename where dt-lag(dt,100) over(partition by id order by dt)>=5
first_value和last_value:irst_value 取分组内排序后,截止到当前行,第一个值,last_value 分组内排序后,截止到当前行,最后一个值
例子:
select name,orderdate,cost,
first_value(orderdate) over(partition by name order by orderdate) as time1,
last_value(orderdate) over(partition by name order by orderdate) as time2
from t_order;
hive的内置函数满足不了所有的业务需求。hive提供很多的模块可以自定义功能,比如:自定义函数、serde、输入输出格式等。而自定义函数可以分为以下三类:
UDF:user defined function
用户自定义函数,一对一的输入输出 (最常用的)。
UDAF:user defined aggregation function:
用户自定义聚合函数,多对一的输入输出,比如:count sum max。
UDTF:user defined table-generate function
用户自定义表生产函数 一对多的输入输出,比如:lateral view explode
开发udf方式
在maven中加入
<dependency>
<groupId>org.apache.hivegroupId>
<artifactId>hive-execartifactId>
<version>2.1.1version>
dependency>
编写UDF函数
注意:(1)继承 org.apache.hadoop.hive.ql.exec.UDF
(2)编写 evaluate()
,这个方法不是由接口定义的,因为它可接受的参数的个数,数据类型都是不确定的。Hive会检查UDF,看能否找到和函数调用相匹配的evaluate()方法
import org.apache.hadoop.hive.ql.exec.UDF;
public class ConcatString extends UDF {
public String evaluate(String str) {
return str + "!";
}
}
打包
加载使用
命令加载(值针对当前session有效)
1. 将编写好的UDF打包并上传到服务器,将jar包添加到hive的classpath中
hive> add jar /data/first.jar;
2. 创建一个自定义的临时函数名
hive> create temporary function myConcatStr as 'com.su.hive.udf.FirstUDF';
3. 查看我们创建的自定义函数,
hive> show functions;
4.在hive中使用函数进行功能测试
hive> select myupper('a');
5. 如何删除自定义函数?在删除一个自定义函数的时候一定要确定该函数没有调用
hive> drop temporary function if exists myConcatStr;
启动参数加载(只针对当前session有效)
1. 将编写好的自定义函数上传到服务器
2. 写一个配置文件,将添加函数的语句写入配置文件中,hive在启动的时候加载这个配置文件
[root@master ~]# vim $HIVE_HOME/conf/hive-init
文件中的内容如下
add jar /data/first.jar;
create temporary function myUpper as 'com.qf.hive.udf.FirstUDF';
3. 启动hive时
[root@master ~]# hive -i $HIVE_HOME/conf/hive-init
配置文件加载(所有皆可用)
1、将编写好的自定函数上传到服务器
2、在hive的安装目录下的bin目录中创建一个文件,文件名为.hiverc
[root@master hive]# vim $HIVE_HOME/bin/.hiverc
3、将添加函数的语句写入这文件中
add jar /data/first.jar;
create temporary function myUpper as 'com.qf.hive.udf.FirstUDF';
4、直接启动hive
创建索引:
create index index_rate2
on table rate2(uid)
as 'compact' -- 索引文件的存储格式
with deferred rebuild -- 索引能够重建
;
修改索引(重建索引):目的产生索引文件
alter index index_rate2 on rate2 rebuild;
查看索引:
show index on rate2;
创建联合索引:
create index index_rate2_uid_movie
on table rate2(uid,movie)
as 'bitmap'
with deferred rebuild;
alter index index_rate2_uid_movie on rate2 rebuild;
删除索引
drop index index_rate2 on rate2;
创建视图:
create view if not exists v_1 as select uid,movie from rate2 where uid <3;
查看视图
show tables;
show create table v_1;
desc v_1;
对视图进行查询时:只能使用视图中的字段。不可以使用视图中没有的字段。
视图是否可以克隆?
删除视图
drop view if exists v_1;
注意:
文件读取/解析的方式:
在建表语句中,指定了记录的切分格式以及字段的切分符号。实际上,hive在解析文件的时候,涉及到了两个类型。
row format
: 用于指定使用什么格式解析一行的数据delimited
: 表示使用 org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe
进行的内容解析fields terminated by
: 表示用什么字符进行字段之间的分隔lines terminated by
: 表示用什么字符进行行之间的分隔强调:hive在select时,这个过程是将字节序列转成hive中的java对象。
hive在insert时,这个过程是将hive中的java对象转成字节序列。
Serde简介
注意,“key”部分在读取时会被忽略,而在写入时始终是常数。基本上Row对象存储在“值”中。
注意,org.apache.hadoop.hive.serde是一个过时的SerDe库。使用最新版本的org.apache.hadoop.hive.serde2。
常用的Serde
LazySimpleSerDe:
默认Hive分隔符**
在Hive中,建表时一般用来来指定字段分隔符和列分隔符。一般导入的文本数据字段分隔符多为逗号分隔符或者制表符(但是实际开发中一般不用着这种容易在文本内容中出现的的符号作为分隔符),当然也有一些别的分隔符,也可以自定义分隔符。有时候也会使用hive默认的分隔符来存储数据。在建表时通过下面语法来指定:
ROW FORMAT DELIMITED FIELDS TERMINATED BY ' ' -- 指定列分隔符
LINES TERMINATED BY '\n' -- 指定行分隔符
CSV
默认分隔符:
列分隔符:逗号
CSV格式的文件也称为逗号分隔值(Comma-Separated Values,CSV,有时也称为字符分隔值,因为分隔字符也可以不是逗号。),其文件以纯文本形式存储表格数据(数字和文本)。CSV文件由任意数目的记录组成,记录间以换行符分隔;每条记录由字段组成,字段间的分隔符是其它字符或字符串,最常见的是逗号或制表符。通常,所有记录都有完全相同的字段序列。
在CSV的Serde中有以下三个默认属性
1. 默认转义字符(DEFAULT_ESCAPE_CHARACTER): \ <--反斜线
2. 默认引用字符(DEFAULT_QUOTE_CHARACTER): " <--双引号
3. 默认分隔符(DEFAULT_SEPARATOR): , <-逗号
json
add jar ./data/json-serde-1.3-jar-with-dependencies.jar;
Regex Serde
hive默认情况下只支持单字节分隔符,如果数据中的分隔符是多字节的,则hive默认是处理不了的。需要使用正则Serde
create table if not exists t_regex(
id string,
uname string,
age int
)
row format serde 'org.apache.hadoop.hive.serde2.RegexSerDe'
with serdeproperties(
'input.regex'='(.*)\\|\\|(.*)\\|\\|(.*)',
'output.format.string'='%1$s %2$s %3$s'
)
stored as textfile;
hive的存储格式分为两大类:一类纯文本文件,一类是二进制文件存储。
配置项
<property>
<name>hive.default.fileformatname>
<value>TextFilevalue>
<description>
Expects one of [textfile, sequencefile, rcfile, orc].
Default file format for CREATE TABLE statement. Users can explicitly override it by CREATE TABLE ... STORED AS [FORMAT]
description>
property>
HQL语句最终会被编译成Hadoop的Mapreduce job,因此hive的压缩设置,实际上就是对底层MR在处理数据时的压缩设置。
map阶段的设置, 就是在MapReduce的shuffle阶段对mapper产生的中间结果数据压缩 。 在这个阶段,优先选择一个低CPU开销的算法。
<property>
<name>hive.exec.compress.intermediatename>
<value>falsevalue>
property>
<property>
<name>hive.intermediate.compression.codecname>
<value><value/>
property>
<property>
<name>hive.intermediate.compression.typename>
<value><value/>
property>
即对reduce阶段的输出数据进行压缩设置。
<property>
<name>hive.exec.compress.outputname>
<value>falsevalue>
property>
注意:如果开启,默认使用中间压缩配置的压缩编码器和压缩类型。
压缩格式 | 压缩比 | 压缩速度 | 需要安装 | 支持切分 |
---|---|---|---|---|
bzip2 | 最高 | 慢 | 否 | 是 |
gzip | 很高 | 比较快 | 否 | 否 |
snappy | 比较高 | 很快 | 是 | 否 |
lzo | 比较高 | 很快 | 是 | 是(需要建立索引) |
压缩格式 | 压缩编码器 |
---|---|
deflate | org.apache.hadoop.io.compress.DefaultCodec |
gzip | org.apache.hadoop.io.compress.GzipCodec |
bzip2 | org.apache.hadoop.io.compress.BZip2Codec |
lzo | com.hadoop.compression.lzo.LzopCodec(中间输出使用) |
snappy | org.apache.hadoop.io.compress.SnappyCodec(中间输出使用) |
案例测试:
# 开启中间压缩机制
hive (mydb)> set hive.exec.compress.intermediate=true;
# 设置中间压缩编码器
hive (mydb)> set hive.intermediate.compression.codec=org.apache.hadoop.io.compress.DefaultCodec;
# 设置压缩类型
hive (mydb)> set hive.intermediate.compression.type=RECORD;
# 开启reduce端的压缩机制
hive (mydb)> set hive.exec.compress.output=true;
12345678
create external table if not exists stocks_seq_2 (
exchange1 string,
symbol string,
ymd string,
price_open float,
price_high float,
price_low float,
price_close float,
volume int,
price_adj_close float
)
row format delimited
fields terminated by ','
stored as sequencefile;
--动态加载数据:
insert into stocks_seq_2 select * from stocks_1;
验证数据是否变小了..........
什么是可分割
在考虑如何压缩那些将由MapReduce处理的数据时,考虑压缩格式是否支持分割是很重要的。考虑存储在HDFS中的未压缩的文件,其大小为1GB,HDFS的块大小为128MB,所以该文件将被存储为8块,将此文件用作输入的MapReduce作业会创建1个输人分片(split,也称为“分块”。对于block,我们统一称为“块”。)每个分片都被作为一个独立map任务的输入单独进行处理。
现在假设,该文件是一个gzip格式的压缩文件,压缩后的大小为1GB。和前面一样,HDFS将此文件存储为8块。然而,针对每一块创建一个分块是没有用的,因为不可能从gzip数据流中的任意点开始读取,map任务也不可能独立于其他分块只读取一个分块中的数据。gzip格式使用DEFLATE来存储压缩过的数据,DEFLATE将数据作为一系列压缩过的块进行存储。问题是,每块的开始没有指定用户在数据流中任意点定位到下一个块的起始位置,而是其自身与数据流同步。因此,gzip不支持分割(块)机制。
在这种情况下,MapReduce不分割gzip格式的文件,因为它知道输入是gzip压缩格式的(通过文件扩展名得知),而gzip压缩机制不支持分割机制。因此一个map任务将处理16个HDFS块,且大都不是map的本地数据。与此同时,因为map任务少,所以作业分割的粒度不够细,从而导致运行时间变长。
Hive常用调优方法