1、hive简介
hive:由facebook开源用于解决海量结构化数据的统计工具。
hive是基于Hadoop的数据仓库工具,可以将结构化的数据文件映射为一张表,并提供sql查询功能。
2、hive本质
- hive的本质是HQL(HiveSQL)转化成MapReduce程序
- hive处理的数据存储在HDFS
- hive分析数据底层实现是MapReduce
- 执行程序运行在YARN
3、hive优缺点
优点:
- 操作接口采用类SQL语法,提供快速开发能力(简单,易上手)
- 避免去写MapReduce,减少开发人员的的学习成本
- hive优势在于处理大数据,对于处理小数据没有优势,hive执行延迟高
- hive支持用户自定义函数,用户可以根据自己的需求来实现自己的函数
缺点:
- hive执行延迟高,因此常用于数据分析,对实时性要求不高的场合
- hive的HQL表达能力有限
4、hive与数据库比较
查询语言:针对hive的特性设计了类SQL的查询语言,熟悉SQL的开发者可以方便的使用hive
数据更新:hive是针对数据仓库应用设计的,而数仓的内容读多写少,因此,hive中不建议对数据的改写,所有数据加载时确定好,而数据库中的数据通常是需要修改的,因此使用insert into添加数据,update修改数据。
执行的延迟:hive查询时由于没有索引,需要扫描整个表,因此延迟较高。另一个导致hive延迟较高的因素是MapReduce
数据规模:hive海量数据的支持,对应数据库处理数据量规模较小
5、hive操作
5.1 hive启动
hive
如果没有进入到hive,就执行以下步骤
cd /opt/module/apache-hive-2.1.1-bin/
bin/hive
如果拒绝连接
jps查看下集群,如果没有nameNode等,执行以下代码
cd /opt/module/hadoop-3.3.0/sbin/
./start-all.sh
hadoop集群启动
如果是安全模式则退出安全模式,查看5.6
5.2 hive退出
使用 exit;或者quit;
5.3 常用交互命令(设置成非严格模式,在当前会话生效)
-e 直接调用hiveSQL语句
hive -e “select * from test.emp”;
-f 调用文件中hiveSQL语句
vim hivef.sql
select * from test.emp;
hive -f /home/hadoop/123/hivef.sql > /home/hadoop/123/234/hivef.txt
5.4 在hive交互窗口了查看HDFS文件系统
dfs -ls /
5.5使用JDBC方式连接hive(用来启动DBeaver)
5.5.1 启动hiveserver2服务
screen -S hive --创建虚拟屏
hive --service hiveserver2 --启动服务
ctrl+a+d --退出虚拟屏
5.5.2 使用DBeaver连接hive
5.6 离开安全模式
查看安全模式
hdfs dfsadmin -safemode get
离开安全模式
hdfs dfsadmin -safemode leave
强制离开安全模式
hdfs dfsadmin -safemode forceExit
6、hive数据类型
基本数据类型
- TINYINT 1byte 有符号整数
- SMALLINT 2byte 有符号整数
- INT 4byte 有符号整数
- BIGINT 8byte 有符号整数
- BOOLEAN 布尔类型,TRUE或者FALSE
- FLOAT 单精度浮点数
- DOUBLE 双精度浮点数
- STRING 字符串
- TIMESTAMP 时间类型
集合数据类型
- STRUCT
- MAP
- ARRAY
实例演示
流程:在hive中test数据库下进行建表,linux中创建一个txt,放置表数据,然后把txt数据导入到hive表格中。
数据准备:
1、hive中进行建表;这个l_text会保存到/user/hive/warehouse/test.db/l_test
create table
l_test (name string,
friends array<string>,
children map<string, int>,
address struct<street:string, city:string>
)
row format delimited fields terminated by ','
collection items terminated by '_'
map keys terminated by ':'
lines terminated by '\n';
2、linux准备text.txt数据
注意最左边不要空格
fulaoshi,lilaoshi_luolaoshi,xiaofu:6_xiaofufu:5,ruyilu_guangzhou
lilaoshi,fulaoshi_luolaoshi,xiaoli:7,ruyilu_guangzhou
3、把linux中的text.txt数据导入到hive表格
load data local inpath '/home/hadoop/123/test.txt' into table test.l_test;
4、hive中进行数据查看
select * from l_test;
类型转化
隐式类型转换规则
1、任何整数类型都可以隐式转换为一个范围更广的类型,如TINYINT可以转换成INT,INT可以转换为BIGINT
2、所有整数类型,float和string(数字)类型都可以隐式转换为double
3、tinyint,smallint,int都可以转换为float
显示转换
cast(“1” as int )
select ‘1’+2,cast(‘1’ as int ) + 2;
7、DDL数据定义
7.1 创建数据库
create database test_2;
create database if not exists test_2;--加if not exists防止报错
- 指定HDFS路径(真实数据在HDFS上的存储路径)创建hive数据库;
create database test_a;
该数据库存储位置:/user/hive/warehouse/test_a.db
7.2 查看数据库
- 查看所有数据库
show databases;
- 查看指定数据库
desc database test;
desc database extended test;--更详细描述,有创建时间
7.3 修改数据库创建时间
- 修改数据库创建时间
alter database test_2 set dbproperties('createtime'='20230605');
7.4 删除数据库
- 删除空数据库
dorp database test_2;
- 判断数据库是否存在然后执行删除
drop database if exists test_2;
- 如果库不为空,强制删除
drop database test_2 cascade;
7.5 使用数据库
- 使用数据库
use databasename;
7.6 创建表
- 创建表
create table if not exists
external 外部表
comment 注释
partitioned by 分区
clustered by 分桶
sorted by 对桶中的数据排序
row format delimited fields terminated by ',' --指定列分隔符
collection items terminated by '_' --指定MAP STRUCT ARRAY的元素分隔符
map keys terminated by ':' --指定map中key和value的分隔符
lines terminated by '\n' --指定行分隔符
stored as 指定文件类型:textfile(文本),rcfile(列式存储格式文件),sequencefile(二进制序列文件)
as 后跟查询语句,根据查询结果创建表
like 允许用户复制现有表的结构,但不复制数据
7.6.1 管理表(内部表)
默认创建的表都是管理表(内部表)。hive会控制着数据的生命周期,当我们删除一个管理表(内部表)时,hive会删除这个表中的数据。(元数据和真实数据都会被删除)
管理表不适合与其他工具共享数据
(1)普通创建表
create table if not exists
hsstd (id int,name string)
row format delimited fields terminated by '\t'
stored as textfile
把数据导入到hsstd
load data local inpath '/home/hadoop/123/hsstd.txt' into table hsstd;
(2)根据查询结果创建表(查询的结果会添加到新创建的表中)
- 从hive的表格查询数据插入到hive表hsstd_2中
create table if not exists hsstd_2 as select id,name from hsstd where id<8;
- 删除表
drop table hsstd_2;
- 创建有分隔符的表
create table if not exists hsstd_2
row format delimited fields terminated by '\t'
as select id,name from hsstd where id<8;
- 根据已经存在的表结构创建表
create table if not exists hsstd_3 like hsstd;
- 查看表的类型
desc formatted hsstd;
7.6.2 外部表
因为表是外部表,所有hive认为并非完全拥有这份真实数据,删除该表并不会删除掉真实数据,不过描述表的元数据会被删掉。(删除元数据,保留真实数据)
实例:
(1)创建外部表
create external table if not exists
dept2(deptno int,dname string,loc int)
row format delimited fields terminated by '\t';
(2)删除外部表
drop table dept2;
7.6.3 内外表相互转换
内部表转换为外部表
alter table dept2 set tblproperties('EXTERNAL'='TRUE');
外部表转换为内部表
alter table hsstd_2 set tblproperties('EXTERNAL'='FALSE');
--FALSE可以替换成不会TRUE的值
7.7 修改表
- 添加列
alter table hsstd_2 add columns (age int);
- 更新列(重命名)
alter table hsstd_2 change column age age2 string;
- 替换列
alter table hsstd_2 replace columns (id int, name2 string, age string);
- 增加一列(如果上述进行替换列操作时,替换列少与原有列,相当与删除了部分列,但是增加列的时候,增加的列是原先删除列,原有列的数据会重新保留下来。个人猜测应该是替换列的时候元数据是保留了表数据的,所有添加的时候数据还会存在)
alter table xxx add columns (user_name string);
- 将增加的列放到某一列后面(换列的时候数据不会跟着动)
alter table xxx add columns (user_name string);
- 将增加的列放到第一列的位置
alter table xxx add columns (user_name string);
7.8 清除表中的数据(truncate)
只会删除管理表中的数据,不会删除外部表的数据
truncate table dept2;
8、DML数据操作
8.1数据导入(hive最小操作级别是文件级的,不能操作行,修改添加只会产生文件)
8.1.1向表中装载数据load
load date : 表示加载数据
local : 表示从本地加载到hive表,否则从HDFS加载到hive(加local是从linux,不加是从HDFS剪切)
inpath:加载数据的路径
overwrite:覆盖表中已有数据,否则追加
into table:表示加载到哪张表
partition:表示上传到指定分区
- 加载本地数据到hive
load data local inpath '/home/hadoop/123/hsstd.txt' into table test.hsstd;
- 上传文件到HDFS
hive中进行操作
默认上传到hdfs的/user/hadoop位置下
dfs -put /home/hadoop/123/hsstd.txt /hxx;
linux中进行上传
指定到了/shentu位置下
hadoop fs -put /home/hadoop/123/hsstd.txt /hxx
- 加载HDFS的文件到hive表中
load data inpath '/hxx/hsstd.txt' into table test.hsstd;
- 加载数据使用overwrite覆盖表中已有数据
load data local inpath '/home/hadoop/123/hsstd.txt' overwrite into table test.hsstd;
8.1.2 通过查询语句向表中插入数据(insert)
insert into table hsstd values (11,'hs11'),(12,'hs12');
insert overwrite table hsstd select id,name from hsstd where id<8;
8.1.3 多表(多分区)插入模式
from hsstd
insert overwrite table hsstd_3
select id,name
insert overwrite table hsstd_4
select id,name where id<5;
8.1.4 import导入(空表或不存在的表)
只有export导出的表目录才能使用import
1、先export导出现有表
export table hsstd3 to '/hxx/hsstd_dc3';
2、根据1中导出的目录,import导入新的表
import table hsstd5 from '/hxx/hsstd_dc3';
8.2 数据导出
(1)将查询结果导出到linux本地(无格式化)
insert overwrite local directory
'/home/hadoop/123/hsstd_dc3'
select * from hsstd;
(2)将查询结果格式化导出到Linux本地
insert overwrite local directory
'/home/hadoop/123/hsstd_dc4'
row format delimited fields terminated by '\t'
(3)将查询结果导出到HDFS
insert overwrite directory
'/hxx/hsstd_dc6'
row format delimited fields terminated by '\t'
select * from hsstd;
(4)export导出到HDFS上
export table test.hsstd to '/hxx/hsstd_dc2';
9、查询(这部分跟oracle差别不大)
9.1基本查询
9.1.1全表查询
select * from hsstd;
select id,name from hsstd;
9.1.2特定列查询
select name from hsstd;
注意
(1)大小写不敏感
(2)SQL可以写在一行或者多行
(3)关键字不能被缩写也不能分行
(4)各子句一般分行写
(5)使用缩进提高语句可读性
9.1.3列别名
select id as st_id,name from hsstd;
9.1.4算术运算符
+ - * / % & | ^ ~
select sal + 10000 from emp_2;
9.1.5 常用聚合函数
count sum min max avg
select count(*) cnt from emp_2;
9.1.6 where语句
select * from emp_2 where sal > 1000;
9.1.7比较运算符
A=BA<=>B 如果A和B都为NULL则返回TRUE,如果一边为NULL返回FALSEA<>B,A!=BA<BA<=BA>BA>=BA NOT BETWEEN B AND CA IS NOT NULLA NOT IN (B,C)select * from emp_2 where sal between 500 and 1000;select * from emp_2 where mgr <=> comm;select * from emp_2 where mgr is null and comm is null;
9.1.8 LIKE和RLIKE
RLIKE通过java实现正则表达式
select * from emp_2 where ename rlike '[A]';
select * from emp_2 where ename like '%A%';
9.1.9逻辑运算符
AND OR NOT
select * from emp_2 where sal > 1000 and deptno = 30;
select * from emp_2 where sal > 1000 or deptno = 30;
select * from emp_2 where deptno not in(20,30);
9.2分组
9.2.1 GROUP BY
select t.deptno,avg(t.sal) avg_sal from emp_2 t group by t.deptno;
9.2.2 HAVING
select t.deptno,avg(t.sal) avg_sal from emp_2 t group by t.deptno having avg(t.sal) > 2000;
9.3JOIN
- 内连接
只有进行连接的两个表中都存在与连接条件相匹配的数据才会被保留下来。(取交集)
select e.empno,e.ename,d.deptno,d.dname from emp_2 e join dept_2 d on e.deptno=d.deptno;
- 左外连接
JOIN操作符左边表中符合where子句的所有记录都会被返回。(左表为主表,驱动表。)
select e.empno,e.ename,d.deptno,d.dname from emp_2 e left join dept_2 d on e.deptno=d.deptno;
- 右外连接
JOIN操作符右边表中符合where子句的所有记录都会被返回。(右表为主表,驱动表。)
select e.empno,e.ename,d.deptno,d.dname from dept_2 d right join emp_2 e on e.deptno=d.deptno;
- 满外关联
将会返回所有表中符合where条件的所有记录,如果任一表的指定字段没符合条件的话,那么使用NULL替代。
select e.empno,e.ename,d.deptno,d.dname from emp_2 e full join dept_2 d on e.deptno=d.deptno;
- 多表连接
select e.ename,d.dname,l.loc_name
from emp_2 e
join dept_3 d
on e.deptno=d.deptno
join location_2 l
on d.loc=l.id;
- 笛卡尔积
在下面条件下产生
(1)省略连接条件
(2)连接条件无效
(3)所有表中的所有行相互连接
select e.empno,e.ename,d.deptno,d.dname from emp_2 e,dept_2 d;
9.4排序
9.4.1全局排序(order by)
order by:全局排序,只有一个Reduce(效率不高,都在一个reduce里面)
可以升序(默认)ASC、降序DESC、可以使用字段别名进行排序,因为执行顺序在select之后
select * from emp_2 order by sal;
select ename,sal * 2 twosal from emp_2 order by twosal desc;
9.4.2每个reduce内部排序(sort by + 加排序的字段)
- 查看reduce数量:
set mapreduce.job.reduces;
- 设置reduce数量:
set mapreduce.job.reduces=3;
- 加载sort by结果到本地
insert overwrite local directory
'/home/hadoop/123/sortby-result'
row format delimited fields terminated by '\t'
select * from emp_2 sort by deptno desc;
9.4.3分区(distribute by)
在有些情况下,需要控制某个特定的行进入指定reducer,为了后续的操作,结合sort by使用
- 查看reduce数量:
set mapreduce.job.reduces;
- 设置reduce数量:
set mapreduce.job.reduces=3;
insert overwrite local directory
'/home/hadoop/123/sortby-result2'
row format delimited fields terminated by '\t'
select * from emp_2 distribute by deptno sort by empno desc;
9.4.4cluster by
当distribute by和sort by字段相同时,可以使用cluster by 只能正序
- 查看reduce数量:
set mapreduce.job.reduces;
- 设置reduce数量:
set mapreduce.job.reduces=3;
- 实例:
insert overwrite local directory
'/home/hadoop/123/sortby-result3'
row format delimited fields terminated by '\t'
select * from emp_2 cluster by deptno;
10、分区分桶
10.1 分区
hive中的分区就是分目录,把一个大的数据集根据业务分割成小的数据集(用的是真实数据),在查询时通过where子句表达式选择查询所需的指定分区,这样查询效率更高。
10.1.1 创建分区表
分区字段不能存在于表中,可以将分区字段视为伪列
create table if not exists
dp (id int,
deptno string,
dname string,
loc string)
partitioned by (day string)
row format delimited fields terminated by '\t';
10.1.2 加载数据
load data local inpath '/home/hadoop/123/dept.txt' into table dp partition(day='20240629')
10.1.3 查看分区数据
hive (test)> select * from dp(表名) where day=20240628
hive (test)> select * from dp where day=20240628 > union> select * from dp where day=20240629;
10.1.4 增加分区(DDL语句可以修改元数据)
hive (test)> alter table dp add partition(day=20240627);
添加多个分区
hive (test)> alter table dp add partition(day=20240627) partition(day=20240626);
10.1.5 删除分区
hive (test)> alter table dp drop partition(day=20240629);
10.1.6 查看分区表有多少个分区
hive (test)> show partitions dp;
OK
partition
day=20240626
day=20240627
day=20240628
Time taken: 0.212 seconds, Fetched: 3 row(s)
10.1.7 查看分区表结构
hive (test)> desc formatted dp;
10.1.8 二级分区
创建分区
create table if not exists
dp2 (id int,
deptno string,
dname string,
loc string)
partitioned by (day string,hour string)
row format delimited fields terminated by '\t';
加载数据
load data local inpath '/home/hadoop/123/dept.txt' into table dp2 partition(day=20240629,hour=13);
查询二级分区表
select * from dp2 where day=20240629 and hour=13;
10.1.9 动态分区
关系数据库中,对分区表insert数据时,数据库会自动根据分区字段的值,将数据插入到对应的分区中,hive页提供了类似的机制,即动态分区(Dynamic Partition),使用hive动态分区需要进行相应的设置。
- 开启动态分区功能(默认TRUE 开启)
查看set hive.exec.dynamic.partition
- 设置非严格模式,默认是strict,表示必须指定至少一个分区为静态分区,nonstrict模式表示允许所有的分区字段都可以使用动态分区
set hive.exec.dynamic.partition.mode=nonstrict;
- 在所有执行MR的节点上,最大一共可以创建多少个分区。默认1000
查看set hive.exec.max.dynamic.partitions;
- 在每个执行MR的节点上,最大一共可以创建多少个分区。默认100
查看set hive.exec.max.dynamic.partitions;
- 整个MR job中,最大可以创建多少个HDFS文件,默认10000
查看set hive.exec.max.dynamic.partitions;
- 当有空分区生成时,是否抛出异常,一般不需要设置。默认FALSE
查看set hive.error.on.empty.partition;
实例
- 建立表
create table if not exists
dp3 (id int,
deptno string,
dname string)
partitioned by(loc string)
row format delimited fields terminated by '\t'
- 插入动态分区
hive (test)> insert into table dp3 partition(loc)> select id,deptno,dname,loc from dp;
- 注意select最末尾的名字要跟动态分区名对应上。
- 没有指定分区的话,动态分区会在执行时,有系统自动分配。
- 动态分区和混合分区「不能直接使用load的方式加载数据」,所以需要使用「查询表」的方式来对数据进行添加
动态分区脚本实例静态分区动态分区解析
- 插入静态分区(通过指定分区名)
insert into table dept_partition_dy partition(loc='test')
select id,deptno,dname from dept_partition;
- 查看目标分区表的分区情况
show partitions dept_partition_dy;
10.2 分桶(桶与桶进行拼接,加快拼接效率)
分桶是将数据集分解成更容易管理的若干部分的另一种技术,是比分区更细粒度的数据范围划分(将表的数据按照哈希函数的结果进行划分和存储的一种方式,通过分桶可以将数据均匀的分布到不同的桶中,提高查询的并行度和技能)。
分区针对的是数据的存储路径,分桶针对的是数据文件。
- 创建分桶表
create table if not exists
stu_buck(id int,
name string)
clustered by (id)
into 4 buckets
row format delimited fields terminated by '\t';
- 设置reduce数量:
set mapreduce.job.reduces=-1;
(reduce的个数设置为-1,让job自行决定有多少个reduce) - 加载数据
先创建一个临时表stu_buck_temp,把数据加载到stu_buck_temp中
load data local inpath '/home/hadoop/123/hxx/t.txt' into table stu_buck_temp;
然后通过insert into 把数据插入到桶中
insert into table stu_buck select id,name from stu_buck_temp;
注意不能直接load data到桶中,它导入的数据不具备分桶结构
- 查看分桶表结构
desc formatted stu_buck;
- 查询分桶表数据
select * from stu_buck;
注意不要使用本地模式,可进行查看set hive.exec.mode.local.auto;
11、函数
11.1系统内置函数
- 查看系统自带函数
show functions; - 显示自带函数用法
desc function upper;
desc function extended upper;
11.2 行转列
- concat(string A,string B):返回输入字符串连接后的结果,支持任意个输入字符串
- concat_ws($eparator,str1,str2):是特殊形式的concat(),第一个参数是所有字符串的分隔符
- collect_set(col):函数只接受基本数据类型,作用主要将某个字段的值进行去重汇总,产生arrry类型字段
实例
原数据格式
fulaoshi 白羊座 A
lilaoshi 处女座 A
chenlaoshi 白羊座 B
luolaoshi 白羊座 A
azhen 处女座 A
aqiang 白羊座 B
需求格式
白羊座,A fulaoshi|luolaoshi
白羊座,B chenlaoshi|aqiang
处女座,A lilaoshi|azhen
建表语句
hstc (name string,
constellation string,
blood_type string)
row format delimited fields terminated by '\t'
加载数据
load data local inpath '/home/hadoop/123/hstc.txt' into table hstc;
方法一:
select t1.c_b,concat_ws('|',collect_set(t1.name)) name_lt
from
(select name,concat_ws(',',constellation,blood_type) c_b from hstc) t1
group by t1.c_b;
方法二:
select concat_ws(',',constellation,blood_type),concat_ws('|',collect_set(name)) from hstc group by constellation,blood_type;
11.3列转行
- explode(col):将hive中一列复杂的array或者map结构拆分成多行
- lateral view 用法:lateral view udtf(expression) tableAlias AS columnAlias
解释:用于和split explode等UDTF函数一起使用,它能将一列数据拆成多行数据,在此基础上可以对拆分后的数据进行聚合
实例
原数据形式
教父 黑帮,警匪,心理,剧情
侏罗纪公园 动作,科幻,剧情,灾难
战狼 战争,动作,灾难
需求形式
教父 黑帮
教父 警匪
教父 心理
教父 剧情
侏罗纪公园 动作
侏罗纪公园 科幻
侏罗纪公园 剧情
侏罗纪公园 灾难
战狼 战争
战狼 动作
战狼 灾难
解法:
select movie,category_name from movie_info
lateral view
explode(split(category,',')) movie_info_tmp as category_name;
11.4 自定义函数
- UDF(user defined function) 一进一出
- UDAF(user defined aggregation function) 聚集函数,多进一出
- UDTF(user defined table-generating function) 表值函数,一进多出
11.5 开窗函数
- OVER():指定分析函数工作的数据窗口大小,这个数据窗口大小可能会随着行的变化而变化
- CURRENT ROW:当前行
- n PRECEDING:往前n行
- n FOLLOWING:往后n行
- UNBOUNDED:起点
- UNBOUNDED PRECEDING:表示从前面的起点
- UNBOUNDED FOLLOWING:表示从后面的起点
- LAG(col,n,default_value):往前第n行数据
- LEAD(col,n,default_value):往后第n行数据
- NTILE(n):把有序窗口的行分发到指定数据的组中,各个组有编号,编号从1开始,对于每一行,NTILE返回此行所属的组的编号。
需求
1.查询在2022年4月购买过的客户及总人数;
select name,count(*) over()
from business
where substring(orderdate,1,7)='2022-04'
group by name;
2.查询客户的购买明细及月购买总额;
select name,orderdate,cost,sum(cost) over(partition by month(orderdate)) from business;
3.上述的场景,每个客户的cost按照日期进行累加;
select name,orderdate,cost,sum(cost) over() as s1, --所有行相加sum(cost) over(partition by name) as s2, --按name分组,组内数据相加sum(cost) over(partition by name order by orderdate) as s3, --按name分组,组内数据累加sum(cost) over(partition by name order by orderdate rows between UNBOUNDED PRECEDING and CURRENT ROW) as s4, --和s3效果相同个,由起点到当前行聚合sum(cost) over(partition by name order by orderdate rows between 1 PRECEDING and CURRENT ROW) as s5, --当前行和前1行进行聚合sum(cost) over(partition by name order by orderdate rows between 1 PRECEDING and 1 FOLLOWING) as s6, --当前行,前一行及后一行聚合sum(cost) over(partition by name order by orderdate rows between CURRENT ROW and UNBOUNDED FOLLOWING) as s7 --当前行及后面所有行聚合from business;
4.查询每个客户上次的购买时间;
select name,orderdate,cost,
lag(orderdate,1,'1970-01-01') over(partition by name order by orderdate) as t1,
lag(orderdate,2) over(partition by name order by orderdate) as t2
from business;
排名类函数
- RANK():排序相同时会重复,总数不变(1224)
- DENSE_RANK():排序相同时会重复,总数会减少 (1223)
- ROW_NUMBER():会根据顺序计算(1234)
select name,subject,score,
rank() over(partition by subject order by score desc) rk,
dense_rank() over(partition by subject order by score desc) de_rk,
row_number() over(partition by subject order by score desc) row_rk
from score;