Apache Hive 从入门到放弃
Hive简介
-
Hive是什么
- 基于Hadoop的数据仓库工具,可以用SQL查询Hive表
- 实现了类似SQL的HiveQL,使得开发MapReduce应用更简单
- 示例:SELECT * FROM table LIMIT 10;
-
Hive的优点
- 易于使用,通过SQL开发大数据应用
- 良好的扩展性,可处理PB级别的数据
- 支持表join,group by, order by等复杂操作
- 支持用户自定义函数,可扩展系统功能
-
Hive架构概览
- 元数据存储在数据库如MySQL中
- 解释器、编译器、优化器等组件负责编译查询
- 使用MapReduce或Spark执行查询
- 结果输出到HDFS或数据库
Hive安装
下载和安装Hive
在Linux服务器上安装wget工具: yum install -y wget
通过wget下载Hive安装包:
wget https://dlcdn.apache.org/hive/hive-3.1.3/apache-hive-3.1.3-bin.tar.gz
检查下载的文件完整性:
md5sum apache-hive-3.1.3-bin.tar.gz
# 对比显示的md5码与官网给定的md5码是否一致
创建安装目录,例如:/opt/hive
mkdir /opt/hive
解压缩Hive压缩包到指定目录:
tar -zxvf apache-hive-3.1.3-bin.tar.gz -C /opt/hive
设置环境变量HIVE_HOME:
export HIVE_HOME=/opt/hive/apache-hive-3.1.3-bin
将HIVE_HOME/bin添加到PATH:
export PATH=$HIVE_HOME/bin:$PATH
配置Hive环境
进入Hive配置文件目录
cd $HIVE_HOME/conf
拷贝环境配置模板
cp hive-env.sh.template hive-env.sh
编辑hive-env.sh文件
vi hive-env.sh
在hive-env.sh中配置以下环境变量:
# Hive执行使用的Hadoop路径
HADOOP_HOME=/usr/local/hadoop
# Hive运行使用Java的home
JAVA_HOME=/usr/java/jdk1.8.0_162
# Hive日志目录
HIVE_LOG_DIR=/tmp/hive/logs
# Hive元数据目录
HIVE_METASTORE_WAREHOUSE=/tmp/hive/warehouse
保存退出后,重启Hive使得配置生效
编辑hive-site.xml:vi hive-site.xml
javax.jdo.option.ConnectionURL
jdbc:mysql://localhost:3306/hive?createDatabaseIfNotExist=true
javax.jdo.option.ConnectionDriverName
com.mysql.cj.jdbc.Driver
javax.jdo.option.ConnectionUserName
root
javax.jdo.option.ConnectionPassword
root
hive.metastore.warehouse.dir
/user/hive/warehouse
拷贝MySQL驱动到Hive
如果本地没有MySQL JDBC驱动,需要先下载该驱动再拷贝到Hive。那么具体的步骤为:
从MySQL官网下载对应版本的驱动连接器
例如到 https://dev.mysql.com/downloads/connector/j/
下载mysql-connector-java-8.0.11.tar.gz
解压驱动包获得jar文件
tar zxvf mysql-connector-java-8.0.11.tar.gz
拷贝jar包到Hive的lib目录
cp mysql-connector-java-8.0.11/mysql-connector-java-8.0.11.jar $HIVE_HOME/lib
验证并重启Hive
以上考虑了从MySQL官网下载驱动的情况,提前下载好驱动再拷贝到Hive。
如果本地已经具备了MySQL驱动,则可以直接拷贝,无需下载。
初始化MetaStore Schema
确认MySQL数据库已经创建好,并配置在hive-site.xml中
进入Hive的bin目录
cd $HIVE_HOME/bin
执行Schema初始化
schematool -initSchema -dbType mysql
如果成功会显示如下信息
Metastore connection URL: jdbc:mysql://localhost:3306/hive?createDatabaseIfNotExist=true
Metastore Connection Driver : com.mysql.cj.jdbc.Driver
Metastore connection User: root
Starting metastore schema initialization to 3.1.0
Initialization script hive-schema-3.1.0.mysql.sql
在MySQL数据库中可以查看新建的Hive元数据表,包括:
- DBS - 记录数据库信息
- TBLS - 记录表信息
- COLUMNS_V2 - 记录列信息
- PARTITIONS - 记录分区信息
- SDS - 记录表/分区物理存储信息
- SERDES - 记录序列化/反序列化类信息
- TBL_PRIVS - 记录表权限信息
- GLOBAL_PRIVS - 记录全局权限信息
- ROLES - 记录角色信息
- ROLE_MAP - 记录角色与用户关联信息
- SEQUENCE_TABLE - 记录序列信息
- INDEX_PARAMS - 记录索引信息
- TYPE_FIELDS - 记录类型字段信息
- TYPES - 记录类型信息
- FUNCS - 记录函数信息
- FUNC_RU - 记录函数与角色关联
配置完毕后测试启动
启动Hive命令行客户端
hive
在hive命令行执行显示数据库列表查询
show databases;
期望看到初始化的默认数据库
OK
default
Time taken: 0.025 seconds, Fetched: 1 row(s)
创建一张测试表
create table test(id int);
查看是否创建成功
show tables;
期望看到test表
OK
test
Time taken: 0.01 seconds, Fetched: 1 row(s)
退出hive命令行
quit;
Hive服务部署
Hive的hiveserver2服务的作用是提供jdbc/odbc接口,为用户提供远程访问Hive数据的功能,例如用户期望在个人电脑中访问远程服务中的Hive数据,就需要用到Hiveserver2。
在远程访问Hive数据时,客户端并未直接访问Hadoop集群,而是由Hivesever2代理访问。由于Hadoop集群中的数据具备访问权限控制,所以此时需考虑一个问题:那就是访问Hadoop集群的用户身份是谁?是Hiveserver2的启动用户?还是客户端的登录用户?答案是都有可能,具体是谁,由Hiveserver2的hive.server2.enable.doAs参数决定,该参数的含义是是否启用Hiveserver2用户模拟的功能。若启用,则Hiveserver2会模拟成客户端的登录用户去访问Hadoop集群的数据,不启用,则Hivesever2会直接使用启动用户访问Hadoop集群数据。模拟用户的功能,默认是开启的。
生产环境推荐开启用户模拟功能,因为开启后才能保证各用户之间的权限隔离。
hiveserver2部署
hivesever2的模拟用户功能,依赖于Hadoop提供的proxy user(代理用户功能),只有Hadoop中的代理用户才能模拟其他用户的身份访问Hadoop集群。因此,需要将hiveserver2的启动用户设置为Hadoop的代理用户,配置方式如下:
修改配置文件core-site.xml,然后分发到各台服务器
cd $HADOOP_HOME/etc/hadoop
vim core-site.xml
增加配置如下:
hadoop.proxyuser.hadoop.hosts
*
hadoop.proxyuser.hadoop.groups
*
hadoop.proxyuser.hadoop.users
*
在hive-site.xml文件中添加如下配置信息:
# 打开文件
vim hive-site.xml
# 添加如下内容
hive.server2.thrift.bind.host
hadoop102
hive.server2.thrift.port
10000
启动hiveserver2:bin/hive --service hiveserver2
使用命令行客户端beeline进行远程访问: bin/beeline -u jdbc:hive2://node01:10000 -n hadoop
看到如下界面
Connecting to jdbc:hive2://node01:10000
Connected to: Apache Hive (version 3.1.3)
Driver: Hive JDBC (version 3.1.3)
Transaction isolation: TRANSACTION_REPEATABLE_READ
Beeline version 3.1.3 by Apache Hive
0: jdbc:hive2://node01:10000>
metastore服务
Hive的metastore服务的作用是为Hive CLI或者Hiveserver2提供元数据访问接口。
metastore有两种运行模式,分别为嵌入式模式和独立服务模式。生产环境中,不推荐使用嵌入式模式。因为其存在以下两个问题:
- 嵌入式模式下,每个Hive CLI都需要直接连接元数据库,当Hive CLI较多时,数据库压力会比较大。
- 每个客户端都需要用户元数据库的读写权限,元数据库的安全得不到很好的保证。
嵌入式模式:嵌入式模式下,只需保证Hiveserver2和每个Hive CLI的配置文件hive-site.xml中包含连接元数据库所需要的以下参数即可:
javax.jdo.option.ConnectionURL
jdbc:mysql://hadonode01op102:3306/metastore?useSSL=false
javax.jdo.option.ConnectionDriverName
com.mysql.jdbc.Driver
javax.jdo.option.ConnectionUserName
root
javax.jdo.option.ConnectionPassword
root
独立服务模式:需做以下配置,首先,保证metastore服务的配置文件hive-site.xml中包含连接元数据库所需的以下参数:
javax.jdo.option.ConnectionURL
jdbc:mysql://node01:3306/metastore?useSSL=false
javax.jdo.option.ConnectionDriverName
com.mysql.jdbc.Driver
javax.jdo.option.ConnectionUserName
root
javax.jdo.option.ConnectionPassword
root
其次,保证Hiveserver2和每个Hive CLI的配置文件hive-site.xml中包含访问metastore服务所需的以下参数:
hive.metastore.uris
thrift://node01:9083
❝
注意:主机名需要改为metastore服务所在节点,端口号无需修改,metastore服务的默认端口就是9083。
此时启动Hive CLI,执行shou databases语句,会出现一下错误提示信息:
hive (default)> show databases;
FAILED: HiveException java.lang.RuntimeException: Unable to instantiate org.apache.hadoop.hive.ql.metadata.SessionHiveMetaStoreClient
这是因为我们在Hive CLI的配置文件中配置了hive.metastore.uris参数,此时Hive CLI会去请求我们执行的metastore服务地址,所以必须启动metastore服务才能正常使用。
metastore服务的启动命令如下:hive --service metastore
❝
注意:启动后该窗口不能再操作,需打开一个新的Xshell窗口来对Hive操作。
重新启动 Hive CLI(bin/hive
),并执行shou databases语句,就能正常访问了。
Hive服务启动脚本
前台启动的方式导致需要打开多个Xshell窗口,可以使用如下方式后台方式启动:
- nohup:放在命令开头,表示不挂起,也就是关闭终端进程也继续保持运行状态
- /dev/null:是Linux文件系统中的一个文件,被称为黑洞,所有写入该文件的内容都会被自动丢弃
- 2>&1:表示将错误重定向到标准输出上
- &:放在命令结尾,表示后台运行
一般会组合使用:nohup [xxx命令操作]> file 2>&1 &,表示将xxx命令运行的结果输出到file中,并保持命令启动的进程在后台运行。
nohup hive --service metastore 2>&1 &
nohup hive --service hiveserver2 2>&1 &
# 打开文件
vim $HIVE_HOME/bin/hiveservices.sh
# 添加内容
#!/bin/bash
HIVE_LOG_DIR=$HIVE_HOME/logs
if [ ! -d $HIVE_LOG_DIR ]
then
mkdir -p $HIVE_LOG_DIR
fi
#检查进程是否运行正常,参数1为进程名,参数2为进程端口
function check_process()
{
pid=$(ps -ef 2>/dev/null | grep -v grep | grep -i $1 | awk '{print $2}')
ppid=$(netstat -nltp 2>/dev/null | grep $2 | awk '{print $7}' | cut -d '/' -f 1)
echo $pid
[[ "$pid" =~ "$ppid" ]] && [ "$ppid" ] && return 0 || return 1
}
function hive_start()
{
metapid=$(check_process HiveMetastore 9083)
cmd="nohup hive --service metastore >$HIVE_LOG_DIR/metastore.log 2>&1 &"
[ -z "$metapid" ] && eval $cmd || echo "Metastroe服务已启动"
server2pid=$(check_process HiveServer2 10000)
cmd="nohup hive --service hiveserver2 >$HIVE_LOG_DIR/hiveServer2.log 2>&1 &"
[ -z "$server2pid" ] && eval $cmd || echo "HiveServer2服务已启动"
}
function hive_stop()
{
metapid=$(check_process HiveMetastore 9083)
[ "$metapid" ] && kill $metapid || echo "Metastore服务未启动"
server2pid=$(check_process HiveServer2 10000)
[ "$server2pid" ] && kill $server2pid || echo "HiveServer2服务未启动"
}
case $1 in
"start")
hive_start
;;
"stop")
hive_stop
;;
"restart")
hive_stop
sleep 2
hive_start
;;
"status")
check_process HiveMetastore 9083 >/dev/null && echo "Metastore服务运行正常" || echo "Metastore服务运行异常"
check_process HiveServer2 10000 >/dev/null && echo "HiveServer2服务运行正常" || echo "HiveServer2服务运行异常"
;;
*)
echo Invalid Args!
echo 'Usage: '$(basename $0)' start|stop|restart|status'
;;
esac
# 添加执行权限
chmod +x $HIVE_HOME/bin/hiveservices.sh
# 启动Hive后台服务
hiveservices.sh start
Hive数据类型
- TINYINT: 1字节整型,取值范围:-128 ~ 127
- SMALLINT: 2字节整型,取值范围:-32768 ~ 32767
- INT: 4字节整型,取值范围:-2147483648 ~ 2147483647
- BIGINT: 8字节整型,取值范围:-9223372036854775808 ~ 9223372036854775807
- FLOAT: 单精度浮点数,4字节
- DOUBLE: 双精度浮点数,8字节
- DECIMAL: 准确的小数值,参数指定精度和范围
- STRING: 字符系列,可指定长度
- VARCHAR: 变长字符串,最大长度65535
- CHAR: 定长字符串,长度1~255
- BOOLEAN: true/false
- DATE: 日期格式,yyyy-MM-dd
- TIMESTAMP: 时间戳格式,yyyy-MM-dd HH:mm:ss
-
ARRAY: 索引从0开始的数组,元素类型可以不同
如 ARRAY, ARRAY
-
MAP: key-value映射表格式
如 MAP
-
STRUCT: 字段集合,类型可以不同
如 STRUCT
- ORC: 列式存储数据格式,压缩效率高
- Parquet: 面向分析查询的列式存储格式
- Avro: 序列化数据交换格式
- JSON: 一种文本序列化格式
Hive操作
创建/删除/修改数据库
-
创建数据库
CREATE DATABASE mydb;
-
查看数据库
SHOW DATABASES;
-
删除数据库
DROP DATABASE mydb;
-
修改数据库
ALTER DATABASE mydb SET DBPROPERTIES('comment' = 'db comment');
创建/删除/修改表
-
创建表
CREATE TABLE mytab (col1 STRING, col2 INT) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' STORED AS TEXTFILE;
-
查看表
SHOW TABLES; DESCRIBE mytab;
-
删除表
DROP TABLE mytab;
-
修改表
ALTER TABLE mytab ADD COLUMNS (col3 INT); ALTER TABLE mytab CHANGE col1 col1_new INT;
载入数据
-
加载本地文件到表
LOAD DATA LOCAL INPATH '/tmp/data.txt' OVERWRITE INTO TABLE mytab;
-
通过查询插入数据
INSERT INTO TABLE mytab SELECT * FROM oldtab;
-
创建表时载入数据
CREATE TABLE newtab AS SELECT * FROM mytab;
查询数据
-
全表查询
SELECT * FROM mytab;
-
条件过滤
SELECT * FROM mytab WHERE col1='value';
-
分组和聚合
SELECT COUNT(*) FROM mytab GROUP BY col2;
-
排序和限制
SELECT * FROM mytab ORDER BY col2 DESC LIMIT 10;
修改表结构
-
添加列
ALTER TABLE mytab ADD COLUMNS (newcol STRING);
-
更改列
ALTER TABLE mytab CHANGE COLUMN col1 newcol INT;
-
替换列
ALTER TABLE mytab REPLACE COLUMNS (col1 STRING, col2 INT);
分区表操作
-
创建分区表
CREATE TABLE mytab (col1 STRING, col2 INT) PARTITIONED BY (partcol STRING) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t';
-
加载数据到分区
LOAD DATA LOCAL INPATH '/tmp/data1.txt' INTO TABLE mytab PARTITION (partcol='part1'); LOAD DATA LOCAL INPATH '/tmp/data2.txt' INTO TABLE mytab PARTITION (partcol='part2');
-
查询分区数据
SELECT * FROM mytab WHERE partcol='part1';
-
增加分区
ALTER TABLE mytab ADD PARTITION (partcol='part3') LOCATION '/path/to/part3';
-
删除分区
ALTER TABLE mytab DROP PARTITION (partcol='part1');
桶表操作
-
创建桶表
CREATE TABLE mytab (col1 STRING, col2 INT) CLUSTERED BY (col2) INTO 4 BUCKETS ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t';
-
加载数据到桶表
LOAD DATA LOCAL INPATH '/tmp/data.txt' INTO TABLE mytab;
-
抽样查询
SELECT * FROM mytab TABLESAMPLE(BUCKET 1 OUT OF 4 ON col2);
-
插入数据到指定桶
INSERT INTO TABLE mytab CLUSTER BY (col2) SELECT key, value FROM src LIMIT 100;
-
删除指定桶数据
ALTER TABLE mytab DROP IF EXISTS PARTITION (col2=5);
HiveQL
Data Definition Language (DDL)
-
创建数据库
CREATE DATABASE mydb;
-
创建表
CREATE TABLE mytab (col1 STRING, col2 INT) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t';
-
创建视图
CREATE VIEW myview AS SELECT col1, col2 FROM mytab WHERE col2 > 1;
-
删除数据库/表/视图
DROP DATABASE mydb; DROP TABLE mytab; DROP VIEW myview;
Data Manipulation Language (DML)
-
载入数据
LOAD DATA LOCAL INPATH '/tmp/data.txt' OVERWRITE INTO TABLE mytab;
-
插入数据
INSERT INTO TABLE mytab VALUES ('val1', 2); INSERT INTO TABLE mytab (col1, col2) VALUES ('val2', 3);
-
更新数据
UPDATE mytab SET col1='newval' WHERE col2=2;
-
删除数据
DELETE FROM mytab WHERE col2=3;
查询语言
-
基础查询
SELECT * FROM mytab; SELECT col1, col2 FROM mytab;
-
条件过滤
SELECT * FROM mytab WHERE col1='val';
-
排序和限制
SELECT * FROM mytab ORDER BY col2 DESC LIMIT 5;
-
聚合查询
SELECT COUNT(*) FROM mytab; SELECT MAX(col2) FROM mytab;
-
分组
SELECT col1, COUNT(*) FROM mytab GROUP BY col1;
-
JOIN
SELECT * FROM tab1 JOIN tab2 ON tab1.id = tab2.id;
Hive架构
- 默认内置的Apache Derby数据库
- 也支持MySQL、Postgres等外部数据库
- 存储数据库、表、分区、列的数据类型信息
- metastore服务通过Thrift提供元数据访问接口
- 接收来自CLI/JDBC/ODBC的查询请求
- 进行语法解析生成抽象语法树AST
- 生成逻辑执行计划
- 优化逻辑计划,生成最优物理计划
- 生成MapReduce、Spark或Tez任务
- 向执行引擎提交作业执行
- 对AST进行类型检查、语义分析
- 生成逻辑执行计划
- 针对DDL语句生成元数据操作
- 对逻辑计划进行优化,产出多种物理计划
- 选择代价最低的物理计划
- 物理优化包括join优化、groupby优化等
- 接收物理计划,生成MapReduce/Spark/Tez任务
- 提交给YARN集群执行作业
- 控制作业的生命周期
- 通过列式存储格式实现向量化查询
- CLI(hive shell)
- JDBC/ODBC - 通过JDBC连接HiveServer2
- Web UI - 基于网页的Hive查询接口
- Thrift/JDBC - 元数据访问接口
Hive优化
Join优化
-
将大表设置为桶表,加快JOIN速度
SET hive.optimize.bucketmapjoin = true; SET hive.optimize.bucketmapjoin.sortedmerge = true;
-
使用Map Join,避免reduce端数据shuffle
SET hive.auto.convert.join = true;
数据倾斜
-
使用抽样和分桶,提前识别和缓解数据倾斜
CLUSTERED BY(col) INTO NUM BUCKETS; TABLESAMPLE(BUCKET 1 OUT OF NUM)
-
调整map数为表分桶数的整数倍
SET mapreduce.job.maps=15;
小文件合并
-
使用CombineHiveInputFormat完成合并
SET mapreduce.input.fileinputformat.input.dir.recursive = true;
设置任务并行度
-
设置map数,增加并行度
SET mapreduce.job.maps=20;
-
设置reduce数,增大并行
SET mapreduce.job.reduces=10;
压缩
-
对中间输出进行压缩
SET hive.exec.compress.intermediate=true;
-
对最终输出结果进行压缩
SET hive.exec.compress.output=true;
好的,我用Markdown格式,尽量详细地概述Hive的各种优化策略,并给出参数设置和使用案例:
本地模式
利用本地多核心执行任务,跳过YARN资源调度
设置参数:
hive.exec.mode.local.auto=true
案例:
SET hive.exec.mode.local.auto=true;
SELECT * FROM small_table LIMIT 10;
上述查询会在本地多核心CPU上并行执行
分桶&抽样
利用分桶和抽样解决数据倾斜问题
设置参数:
CLUSTERED BY(col) INTO BUCKETS
TABLESAMPLE(BUCKET x OUT OF y)
案例:
CREATE TABLE page_views
CLUSTERED BY(pageID) INTO 128 BUCKETS;
SELECT * FROM page_views TABLESAMPLE(BUCKET 5 OUT OF 128);
Hive on Spark
Hive on Spark利用Spark作为Hive查询的执行引擎,以实现更高的查询性能
运行原理
配置方法
在hive-site.xml中设置参数:
hive.execution.engine=spark
使用案例
SET hive.execution.engine=spark;
SELECT * FROM tbl;
上述查询会使用Spark作为执行引擎,实现快速查询
优点
- 利用Spark的执行速度优势
- 低延迟,实时查询
- 处理流式数据
- 与机器学习集成
Hive on Spark让Hive可以享受Spark带来的性能优势,构建低延迟和实时的大数据分析平台。
Hive 事务
Hive从0.13版本开始支持基于MVCC的事务功能
ACID特性
Hive表开启事务后,可以支持原子性、一致性、隔离性的事务:
- 支持插入/更新/删除行的事务改变
- 事务隔离,读取可重复读版本的数据视图
- 满足ACID特性,保证事务一致性
事务操作
-- 开始事务
START TRANSACTION;
-- 查询
SELECT * FROM table WHERE id=1 FOR UPDATE;
-- 数据修改
UPDATE table SET col='new_value' WHERE id=1;
-- 提交事务
COMMIT;
需要先设置表为事务表:ALTER TABLE table SET TBLPROPERTIES('transactional'='true');
使用案例
ALTER TABLE table SET TBLPROPERTIES('transactional'='true');
START TRANSACTION;
UPDATE table SET col='v1' WHERE id=1;
UPDATE table SET col='v2' WHERE id=2;
COMMIT;
窗口函数
RANK()
按指定字段排序后进行排名,排名相同会重复
RANK() OVER (PARTITION BY dept ORDER BY salary DESC) AS rank
案例:按部门分区,按薪水降序排名,相同薪水会重复排名
DENSE_RANK()
按指定字段排序后进行排名,排名相同会跳过编号
DENSE_RANK() OVER (PARTITION BY dept ORDER BY salary DESC) AS dense_rank
案例:按部门和薪水排名,相同薪水跳过排名编号
ROW_NUMBER()
按指定字段排序,为每行生成一个连续递增的编号
ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) AS row_num
案例:按部门和薪水给每行生成一个编号,按降序排序
LEAD()/LAG()
获取当前行在窗口分区中往后/前指定偏移量行的字段值
LEAD(salary, 1) OVER (PARTITION BY dept ORDER BY salary DESC)
LAG(salary, 1) OVER (PARTITION BY dept ORDER BY salary DESC)
案例:获取当前行后面/前面一行的salary值
FIRST_VALUE()/LAST_VALUE()
获取窗口分区中排序后第一行/最后一行指定字段的值
FIRST_VALUE(salary) OVER (PARTITION BY dept ORDER BY salary DESC)
LAST_VALUE(salary) OVER (PARTITION BY dept ORDER BY salary DESC)
案例:获取部门薪水排名第一/最后的salary
NTILE()
将窗口分区中的行分发到指定的组中,各组有编号
NTILE(2) OVER (PARTITION BY dept ORDER BY salary DESC)
案例:按薪水排名后,将部门内员工分为2组
CUME_DIST()
计算当前行在窗口分区中的累积分布值(百分位数)
CUME_DIST() OVER (PARTITION BY dept ORDER BY salary DESC)
案例:计算当前薪水在部门中的百分位数排名
PERCENT_RANK()
计算当前行在窗口分区中的百分比排名
PERCENT_RANK() OVER (PARTITION BY dept ORDER BY salary DESC)
案例:计算当前薪水在部门的百分比排名