mysql实现开窗函数

mysql不支持oracle开窗函数的强大功能

 

建表-- 测试数据

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` bigint(20) NOT NULL COMMENT '主键ID',
  `username` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '姓名',
  `password` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '密码',
  `age` int(11) NULL DEFAULT NULL COMMENT '年龄',
  `email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '邮箱',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'Jone', '1', 18, '[email protected]');
INSERT INTO `user` VALUES (2, 'Jack', '2', 20, '[email protected]');
INSERT INTO `user` VALUES (3, 'Tom', '3', 21, '[email protected]');
INSERT INTO `user` VALUES (4, 'Sandy', '4', 20, '[email protected]');
INSERT INTO `user` VALUES (5, 'Billie', '5', 21, '[email protected]');

SET FOREIGN_KEY_CHECKS = 1;

 

1、基础语法

select u.*
,@rownum :=@rownum+1 rownum

from user u, (select @rownum:=0 , @curValue:=null) r

备注:

1. @rownum和@curVal为自定义的用户变量,仅针对当前客户端有效,此处只用rownum;

2. (select @rownum:=0 , @curValue:=null) r 初始化@rownum和@curVal变量;

   (mysql中‘’、0、null均为false);

2、实现 row_number() over(order by )

根据sql的执行顺序可知 变量会最后再计算,所以是先排序好之后,才会开始计算@num

select
@rownum :=@rownum+1 rownum,

u.*

from user u, (select @rownum:=0) r
order by id desc

mysql实现开窗函数_第1张图片

3、实现分组排名效果row_number() over(partition by ? order by ? )

select 
@rownum :=IF(@age=age, @rownum+1, 1) rownum,
@age :=age ageTmp,
u.*
from user u, (select @rownum:=0, @age:=null) r
order by age,id

mysql实现开窗函数_第2张图片

解释:

1、首先通过对需要分组的字段排序,和按分组后的显示顺序字段排序,获得基本数据 

2、因为rownum记录的是每组的顺序值,所以在将rownum赋值值判断age是否为此组的值,不是则重新开始计数

 

--子查询方式【取分组中前N行(排名前几名)】
mysql 相关子查询参考

select * from testGroup as a
where a.ID in (select  ID from testGroup b where a.UserID = b.UserID order by b.OrderID limit 2)
--或者
select * from testGroup a
where not exists (select 1 from testGroup b where a.UserID = b.UserID and a.OrderID > b.OrderID 
having count(1) >= 2)
--或者
select * from testGroup a
where (select count(1) from testGroup b where a.UserID = b.UserID and a.ID >= b.ID) <= 2
--没有唯一标识的表,可以用checksum来标识每行(MSSQL?)
select * from testGroup as a
where checksum(*) in (select top 2 checksum(*) from testGroup b where a.UserID = b.UserID order by b.OrderID)
mysql使用子查询实现
create table test1_1(id int auto_increment primary key,`subject` char(20),score int);
insert into test1_1 values(null,'语文',99),(null,'语文',98),(null,'语文',97);
insert into test1_1 values(null,'数学',89),(null,'数学',88),(null,'数学',87);
insert into test1_1 values(null,'英语',79),(null,'英语',78),(null,'英语',77),(null,'英语',78);

-- 根据成绩,求出每个科目的前2名

select * from test1_1;

select * from test1_1 t1
where (select count(1) from test1_1 t2 where t1.subject=t2.subject and t2.score>=t1.score ) <=2;

理解:相当于子查询中所有值分别与t1中的一个值进行比较,t2中score的值大于t1中此值的数量小于2,则代表每组按照分值从大到小取前两名。

--4、

  rank() over()是跳跃排序,有两个第二名时接下来就是第四名(同样是在各个分组内)

  dense_rank() over()是连续排序,有两个第二名时仍然跟着第三名。相比之下row_number是没有重复值的 .

  分别有分组与不分组排序。只看分组的就可以,上面的数据多加一条 (null,'英语',78) 方便看结果

实现 RANK() OVER (PARTITION BY ke ORDER BY val)

实现 DENSE_RANK() OVER (PARTITION BY ke ORDER BY val)

--变量会最后再计算,所以是先排序好之后,才会开始计算@num

select
CASE
WHEN @str != subject THEN @rownum:= 1
ELSE @rownum:= @rownum + 1
END AS ROW_NUMBER,
CASE
WHEN @str != subject THEN @rank:= 1
WHEN @scoretmp = score THEN @rank
ELSE @rank:= @rownum
END AS RANK,
CASE
WHEN @str != subject THEN @dense_rank:= 1
WHEN @scoretmp = score THEN @dense_rank
ELSE @dense_rank:= @dense_rank + 1
END AS DENSE_RANK,
@str :=subject as subjecttmp,
@scoretemp :=score as scoretemp,
u.*
from sub u,(select @rownum :=0, @rank :=0, @dense_rank :=0, @str :=null, @scoretmp:= 0) r
order by subject,score desc

mysql实现开窗函数_第3张图片 

看着rank和dense_rank的写法没问题,但是此出现的不正确的结果,找到原因再来更改。


--关于数字的case when 验证
 
  mysql实现开窗函数_第4张图片
 
关于字符、字符串 的case when验证

mysql实现开窗函数_第5张图片


 

SELECT
CASE
WHEN @ke != ke THEN @rownum:= 1
ELSE @rownum:= @rownum + 1
END AS ROW_NUMBER,
CASE
WHEN @ke != ke THEN @rank:= 1
WHEN @val = val THEN @rank
ELSE @rank:= @rownum
END AS RANK,
CASE
WHEN @ke != ke THEN @dense_rank:= 1
WHEN @val = val THEN @dense_rank
ELSE @dense_rank:= @dense_rank + 1
END AS DENSE_RANK,
id,
@ke := ke AS ke,
@val := val AS val
FROM
(SELECT @ke:='') k,
(SELECT @val:=0) v,
(SELECT @rownum:=0) r,
(SELECT @rank:=0) r2,
(SELECT @dense_rank:=0) d,
test_rownum main
ORDER BY
ke, val;

 

参考

https://www.cnblogs.com/gered/p/10430829.html

http://blog.sina.com.cn/s/blog_64e2d33e0101ashr.html

你可能感兴趣的:(MySQL)