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 ...
随机推荐
- .NET程序员AI开发基座:Microsoft.Extensions.AI
大家好,我是Edison. 微软在2024年11月就发布了新的AI核心库Microsoft.Extensions.AI,虽然目前还是一个预览版,但其可以大大简化我们的AI集成和开发工作. Micros ...
- nginx 如何强制跳转 https
本项目 nginx 作为代理服务 项目上线,客户说要加个安全证书 ,于是安全证书是加上了,可是htttp和https都能访问网站,客户要求不行必须强制用带有https的地址访问 开整 这是 http ...
- CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
CSnakes 是一个用于在.NET项目中嵌入Python代码的工具,由.NET源生成器和运行时组成,能够实现高效的跨语言调用,Github:https://github.com/tonybalone ...
- 怎么证明二元函数的极限是多少?& 怎么证明二元函数的极限不存在?
怎么证明二元函数的极限是多少:https://zhaokaifeng.com/16589/ 怎么证明二元函数的极限不存在:https://zhaokaifeng.com/16600/
- autMan奥特曼机器人-内置微信如何定时给公众号发消息
autMan版本要求2.1.3以上 一.打开左侧栏的本地开发,然后从实时日志获取公众号的ID或名称 ![2024-10-23T01:45:34.png][1] ![2024-10-23T01:44:5 ...
- Hive - [08] 数据仓库物理模型设计
分区 分区是将表的数据按照某个列的值进行划分和存储的一种方式.通过分区,可以将数据按照特定的维度进行组织,提高查询效率和数据管理的灵活性. 一.分区的优势 提高查询性能:通过分区,可以将数据按照特定的 ...
- Java - JVM及其调优
原文链接:https://blog.csdn.net/qq_27098537/article/details/124436788 一.什么是JVM 用于运行java代码,包括一套字节码指令集.一组寄存 ...
- linux系统升级/更新OpenSSL版本操作流程记录
问题描述:有时OpenSSL版本过老升级,或者需要更新OpenSSL版本 1.登录linux系统后输入openssl version 查看现在使用的版本 我的输入后版本信息为:OpenSSL 1.1. ...
- Jsoncpp的安装与使用方式
JsonCpp 是一个C++库,用于解析和生成JSON数据.它支持解析JSON文件或字符串到C++对象,以及将C++对象序列化回JSON格式. 安装Jsoncpp 我们可以输入以下命令安装jsoncp ...
- openpyxl 写入字典
def write(self,data_path, sheetname,value): index = len(value) workbook = openpyxl.Workbook() sheet ...