Mysql 实现 rank 和 != 问题
我一直相信, 人是能预测未来的, 这应该是前几年看弗洛伊德, 荣格的一些心理学书, 给我的一些感受, 有个片段是关于做梦的, 一个人梦见子弹穿过他自己的头颅, 结果不久, 他就去世了. 这个片段当时给了我很多奇思妙想, 关于人类的潜意识, 也许未来就在潜意识中, 但生活中可能无法察觉到这一点.
开头有点偏了, 也是为了引出一个今天的话题, 我上午有一个面谈, 我冥冥中就感觉会被问到 sql 排序问题, 之前也写过, 做了笔记, 但临场还是给忘了, 然后是一些 GBDT, XGBOOST, 决策树, 随机森林这些话题, 我也是准备没那么充分, 虽说都这些的数学原理我都是推导过的.. 哎...最后也没能再争取一下, 有点难受, 即便如此, 还是给推荐了我的笔记, 希望能遇见伯乐吧, 笔记是真诚的.
Rank 排序实现
排名这块, 我虽然近几个月写了几千行 SQL 了, rank 其实没有写过, Oracle 有这种 rank () 之类的窗口函数, 但 Mysql 是没有的, 要自己来实现一把.
测试数据, 还是用之前联系的 cj.score 表来展示, 数据如下:
mysql> select * from cj.score;
+------+------+-------+
| s_id | c_id | score |
+------+------+-------+
| 0001 | 0001 | 80 |
| 0001 | 0002 | 90 |
| 0001 | 0003 | 99 |
| 0002 | 0002 | 60 |
| 0002 | 0003 | 80 |
| 0003 | 0001 | 80 |
| 0003 | 0002 | 80 |
| 0003 | 0003 | 80 |
+------+------+-------+
8 rows in set (0.00 sec)
思路
首先, 定义两个变量, 就叫 @rank, 和 @pre
- @rank 用于记录排名值, 初始值为 0
- @pre 用于上一条记录的分数 score 值, 初始值为 null.
给自定义变量赋值有2种方法,一种是用set,另一种使用select ; 而且赋值推荐使用 := 这种方式.
select @rank := 0, @pre := null
然后, 对每条数据进行判断.
@rank := if (@pre=score, @rank, @rank+1) as rank,
@pre := score
算法:
大前提: 先要对数据集中, 该排序字段进行降序
for 遍历第一条记录的时候, @rank 值为0, @pre 值为 null:
if @pre = score:
# 排名不变
@rank + 0
else:
#
@rank + 1
当遍历到第二条记录, 此时 @rank = 1, @pre 为上条记录的score值.
if 当前的 score 值 = 上一条的 score 值, 就排名不变嘛, 还是 @rank;
不等于就 @rank + 1
... 这样就保证相同分数的排名相等, 不同排名会使 rank 增加, 且不会出现间隔
实现
-- step1: 定义变量, 并对数据集按 score 降序
-- 相当于把主表, 添加两个字段
select
a.* ,
b.*
from cj.score as a, (select @rank:=0, @pre:=null) as b
order by a.score desc
+------+------+-------+----------+------------+
| s_id | c_id | score | @rank:=0 | @pre:=null |
+------+------+-------+----------+------------+
| 0001 | 0003 | 99 | 0 | NULL |
| 0001 | 0002 | 90 | 0 | NULL |
| 0001 | 0001 | 80 | 0 | NULL |
| 0002 | 0003 | 80 | 0 | NULL |
| 0003 | 0001 | 80 | 0 | NULL |
| 0003 | 0002 | 80 | 0 | NULL |
| 0003 | 0003 | 80 | 0 | NULL |
| 0002 | 0002 | 60 | 0 | NULL |
+------+------+-------+----------+------------+
8 rows in set (0.00 sec)
select
-- 动态来计算 rank 值
a.score ,
@rank:= if(@pre=a.score, @rank+0, @rank+1) as my_rank ,
@pre:=score
-- 0: 给主表添加上两个字段
from cj.score as a, (select @rank:=0, @pre:=null) as b
order by a.score desc
+-------+---------+-------------+
| score | my_rank | @pre:=score |
+-------+---------+-------------+
| 99 | 1 | 99 |
| 90 | 2 | 90 |
| 80 | 3 | 80 |
| 80 | 3 | 80 |
| 80 | 3 | 80 |
| 80 | 3 | 80 |
| 80 | 3 | 80 |
| 60 | 4 | 60 |
+-------+---------+-------------+
8 rows in set (0.00 sec)
搞定, 这样就已经排序出来了. 最后还是来一个完整版的, 把其他字段补全, 不需要显示的 @pre 字段给干掉.
select
c.s_id,
c.c_id,
c.score,
c.my_rank as score_rank
from (
select
a.s_id,
a.c_id,
a.score,
@rank := if(@pre=score, @rank + 0, @rank + 1) as my_rank,
@pre := score
from cj.score as a, (select @rank:=0, @pre=null) as b
order by a.score desc
) as c
+------+------+-------+------------+
| s_id | c_id | score | score_rank |
+------+------+-------+------------+
| 0001 | 0003 | 99 | 1 |
| 0001 | 0002 | 90 | 2 |
| 0001 | 0001 | 80 | 3 |
| 0002 | 0003 | 80 | 3 |
| 0003 | 0001 | 80 | 3 |
| 0003 | 0002 | 80 | 3 |
| 0003 | 0003 | 80 | 3 |
| 0002 | 0002 | 60 | 4 |
+------+------+-------+------------+
8 rows in set (0.00 sec)
关于 SQL != 的问题
我是前段时间, 无意中发现的, 在通常的认知中, "等于" 和 "不等于" 二者应该是 矛盾关系, 是对立统一的, 但无意间发现 Null 的时候, 并非如此.
先来正常的. 我用一个常用的 超市数据集做演示, 有一个字段 category 产品的分类, 先分别统计它的分类值数量:
select
category,
count(category) as cnt
from cj.super_market
group by category
+--------------+------+
| category | cnt |
+--------------+------+
| 办公用品 | 5687 |
| 家具 | 2244 |
| 技术 | 2028 |
+--------------+------+
3 rows in set (0.01 sec)
mysql> select 5687 + 2244 + 2028;
+--------------------+
| 5687 + 2244 + 2028 |
+--------------------+
| 9959 |
+--------------------+
1 row in set (0.00 sec)
可以看到总数: 办公用品 + 家具 + 技术 = 5687 + 2244 + 2028 = 9959
先进行过滤, 将家具排除掉, 理论上, 总数应该是 : 5687 + 2028 = 7715
select count(id) from cj.super_market where category != '家具';
7715
哦.. . 是对的, 我好像之前自己写 SB 了...呀, 心态崩了, 之前以为的 bug 是把 2028 写成了2208 导致错误..
想说的是, 当值有 null 的时候, 要单独来考虑...
我傻了, 弄混淆了 count(*) 是会计算所有的行数, 包括 null 的, 而 count(col) 会忽略 null 的行, 导致总数对不上, 此刻我对自己有些无语...
嗯不举例了, 我现在需要冷静一下, 好好回溯下这个问题.
Mysql 实现 rank 和 != 问题的更多相关文章
- mysql中 Rank、DENSE_RANK()的区别
相同点:RANK()和DENSE_RANK()的是排名函数 不同点:RANK()是跳跃排序,即如果有两条记录重复,接下来是第三级别 如:1 2 2 4,会跳过3 DENSE_RANK()是连续排序,即 ...
- sql语句练习50题(Mysql版)
表名和字段–1.学生表Student(s_id,s_name,s_birth,s_sex) –学生编号,学生姓名, 出生年月,学生性别–2.课程表Course(c_id,c_name,t_id) – ...
- MySQL经典练习题
表名和字段 –1.学生表 Student(s_id,s_name,s_birth,s_sex) –学生编号,学生姓名, 出生年月,学生性别 –2.课程表 Course(c_id,c_name,t_id ...
- MySQL 8.0的关系数据库新特性详解
前言 MySQL 8.0 当前的最新版本是 8.0.4 rc,估计正式版本出来也快了.本文介绍几个 8.0 在关系数据库方面的主要新特性. 你可能已经知道 MySQL 从版本 5.7 开始提供了 No ...
- sql server,mysql,oracle平时用法的区别
由于工作的原因,上家公司一直使用的oracle,后来接触了的几个项目,既有使用mysql的又有使用sqlserver,自己在使用sqlserver及mysql要实现某功能时,经常要在网上找来找去,所以 ...
- -sql语句练习50题(Mysql学习练习版)
–1.学生表 Student(s_id,s_name,s_birth,s_sex) –学生编号,学生姓名, 出生年月,学生性别 –2.课程表 Course(c_id,c_name,t_id) – –课 ...
- sql语句练习50题(Mysql版) 围观
表名和字段 –.学生表 Student(s_id,s_name,s_birth,s_sex) –学生编号,学生姓名, 出生年月,学生性别 –.课程表 Course(c_id,c_name,t_id) ...
- mysql语句练习50题
为了练习sql语句,在网上找了一些题,自己做了一遍,收益颇多.很多地方换一种思路,有更好的写法,欢迎指正. 题目地址:https://blog.csdn.net/fashion2014/article ...
- sql语句练习50题(Mysql版-详加注释)
表名和字段 1.学生表 Student(s_id,s_name,s_birth,s_sex) --学生编号,学生姓名, 出生年月,学生性别 2.课程表 Course(c_id, ...
- Mysql 8.0 新特性测试
Mysql 8.0 新特性测试 Role MySQL8.0版本添加了role特性,role是一种逻辑概念是权限的集合,可以将一个或以上的权限赋予给role,再将role赋给user.Oracle,Po ...
随机推荐
- JUC并发—11.线程池源码分析
大纲 1.线程池的优势和JUC提供的线程池 2.ThreadPoolExecutor和Excutors创建的线程池 3.如何设计一个线程池 4.ThreadPoolExecutor线程池的执行流程 5 ...
- ServerMmon青蛇探针,一个超好用的服务器状态监控-搭建教程
serverMmon(青蛇探针)是nodeJs开发的一个酷炫高逼格的云探针.云监控.服务器云监控.多服务器探针~. 在线演示:http://106.126.11.114:5880/ 主要功能: 全球服 ...
- SICK Ranger3源码分析——断线重连
前言 本文可在https://paw5zx.github.io/SICK-Ranger3-source-code-analysis-01/中阅读,体验更加 简单分析一下SICK Ranger3源码中断 ...
- linux 源码安装完php后在目录下找不到php.ini的问题
首先,我们需要确定php版本的php.ini文件需要放在个目录下 我们先执行以下命令查看php的php.ini目录应该放在那个地方 php -i |grep php.ini 效果如下,现在我们确定好了 ...
- 搭建自己的OCR服务,第二步:PaddleOCR环境安装
PaddleOCR环境安装,遇到了很多问题,根据系统不同问题也不同,不要盲目看别人的教程,有的教程也过时了,根据实际情况自己调整. 我这边目前是使用windows 10系统+CPU + python ...
- SQL Server如何跟踪自动统计信息更新?
SQL Server数据库中,我们都清楚统计信息对于优化器来说非常重要.一般情况下,我们会开启"自动更新统计信息"(Auto Update Statistics)这个选项,以便数据 ...
- Java基于XXLJOB的定时任务实现阶梯式通知方式
数据库表设计 CREATE TABLE `tx_order_push_info` ( `order_no` varchar(64) DEFAULT NULL COMMENT '交易单号', `orde ...
- raw.githubusercontent.com 访问不了
访问 Github 中的 raw 文件内容时会跳转到 raw.githubusercontent.com 这个域名 但是访问不了. 解决办法 Windows 在 C:\Windows\System32 ...
- 移除任务栏右端"显示桌面"按钮-AutoIt
核心代码 $hwnd = WinGetHandle("[CLASS:Shell_TrayWnd]", "") ControlHide($hwnd, " ...
- Linux C线程读写锁深度解读 | 从原理到实战(附实测数据)
Linux C线程读写锁深度解读 | 从原理到实战(附实测数据) 读写锁练习:主线程不断写数据,另外两个线程不断读,通过读写锁保证数据读取有效性. 代码实现如下: #include <stdio ...