我一直相信, 人是能预测未来的, 这应该是前几年看弗洛伊德, 荣格的一些心理学书, 给我的一些感受, 有个片段是关于做梦的, 一个人梦见子弹穿过他自己的头颅, 结果不久, 他就去世了. 这个片段当时给了我很多奇思妙想, 关于人类的潜意识, 也许未来就在潜意识中, 但生活中可能无法察觉到这一点.

开头有点偏了, 也是为了引出一个今天的话题, 我上午有一个面谈, 我冥冥中就感觉会被问到 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 和 != 问题的更多相关文章

  1. mysql中 Rank、DENSE_RANK()的区别

    相同点:RANK()和DENSE_RANK()的是排名函数 不同点:RANK()是跳跃排序,即如果有两条记录重复,接下来是第三级别 如:1 2 2 4,会跳过3 DENSE_RANK()是连续排序,即 ...

  2. sql语句练习50题(Mysql版)

    表名和字段–1.学生表Student(s_id,s_name,s_birth,s_sex) –学生编号,学生姓名, 出生年月,学生性别–2.课程表Course(c_id,c_name,t_id) – ...

  3. MySQL经典练习题

    表名和字段 –1.学生表 Student(s_id,s_name,s_birth,s_sex) –学生编号,学生姓名, 出生年月,学生性别 –2.课程表 Course(c_id,c_name,t_id ...

  4. MySQL 8.0的关系数据库新特性详解

    前言 MySQL 8.0 当前的最新版本是 8.0.4 rc,估计正式版本出来也快了.本文介绍几个 8.0 在关系数据库方面的主要新特性. 你可能已经知道 MySQL 从版本 5.7 开始提供了 No ...

  5. sql server,mysql,oracle平时用法的区别

    由于工作的原因,上家公司一直使用的oracle,后来接触了的几个项目,既有使用mysql的又有使用sqlserver,自己在使用sqlserver及mysql要实现某功能时,经常要在网上找来找去,所以 ...

  6. -sql语句练习50题(Mysql学习练习版)

    –1.学生表 Student(s_id,s_name,s_birth,s_sex) –学生编号,学生姓名, 出生年月,学生性别 –2.课程表 Course(c_id,c_name,t_id) – –课 ...

  7. sql语句练习50题(Mysql版) 围观

    表名和字段 –.学生表 Student(s_id,s_name,s_birth,s_sex) –学生编号,学生姓名, 出生年月,学生性别 –.课程表 Course(c_id,c_name,t_id) ...

  8. mysql语句练习50题

    为了练习sql语句,在网上找了一些题,自己做了一遍,收益颇多.很多地方换一种思路,有更好的写法,欢迎指正. 题目地址:https://blog.csdn.net/fashion2014/article ...

  9. sql语句练习50题(Mysql版-详加注释)

    表名和字段 1.学生表       Student(s_id,s_name,s_birth,s_sex) --学生编号,学生姓名, 出生年月,学生性别 2.课程表       Course(c_id, ...

  10. Mysql 8.0 新特性测试

    Mysql 8.0 新特性测试 Role MySQL8.0版本添加了role特性,role是一种逻辑概念是权限的集合,可以将一个或以上的权限赋予给role,再将role赋给user.Oracle,Po ...

随机推荐

  1. Hive - [08] 数据仓库物理模型设计

    分区 分区是将表的数据按照某个列的值进行划分和存储的一种方式.通过分区,可以将数据按照特定的维度进行组织,提高查询效率和数据管理的灵活性. 一.分区的优势 提高查询性能:通过分区,可以将数据按照特定的 ...

  2. 如何通过 Linux Bash 技术,让你的独立产品实现一键安装

    我在业余时间开发了一款自己的独立产品:升讯威在线客服与营销系统.陆陆续续开发了几年,从一开始的偶有用户尝试,到如今线上环境和私有化部署均有了越来越多的稳定用户. 虽然我编写了非常详细的在线文档,说明如 ...

  3. .NET 10首个预览版发布:重大改进与新特性概览!

    前言 .NET 团队于2025年2月25日发布博文,宣布推出 .NET 10 首个预览版更新,重点改进.NET Runtime.SDK.Libraries .C#.ASP.NET Core.Blazo ...

  4. Selenium KPI接口 附件上传

    实现功能 拖拽图片到百度上传图片搜索功能区域. 定位.send_keys(r'图片路径') 导入相关包 from selenium import webdriver from time import ...

  5. yolov5 train报错:TypeError: expected np.ndarray (got numpy.ndarray)

    前言 mac intel 机器上,使用 yolov5 物体检测训练时报错:TypeError: expected np.ndarray (got numpy.ndarray) 这个错误信息 TypeE ...

  6. Go 1.20更新了那些内容

    PGO的引入 Go 1.20 发布了配置文件引导优化(PGO)的预览版,使编译器能够根据运行时配置文件信息,执行应用程序和工作负载的特定性优化.提供要构建的配置文件,使编译器能够将应用程序的速度提高大 ...

  7. 自定义异常--java进阶day08

    1.自定义异常 2.自定义异常的格式 看你想要定义哪种异常,对应的继承哪种异常类 以我们之前写的代码举例,Exception类过于庞大,所有的异常子类都可以被它接收,这样就会导致无法精确捕获,所以我们 ...

  8. 感觉程序员要被 AI 淘汰了?学什么才有机会?

    感觉程序员要被 AI 淘汰了?学什么才有机会? ️ 推荐观看视频版:https://www.bilibili.com/video/BV1i9Z8YhEja AI 会淘汰程序员么? 我的答案是 &quo ...

  9. chatops

    ChatOps是什么? ChatOps, 简单地说,这是一种方法,允许团队以聊天室的方式来协作和管理其基础结构.代码和数据的许多方面.通过使用聊天机器人和脚本,团队可以执行命令.查询信息,并将知识分发 ...

  10. 一文速通Python并行计算:06 Python多线程编程-基于队列进行通信

    一文速通 Python 并行计算:06 Python 多线程编程-基于队列进行通信 摘要: 队列是一种线性数据结构,支持先进先出(FIFO)操作,常用于解耦生产者和消费者.慢速生产-快速消费场景中,队 ...