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

开头有点偏了, 也是为了引出一个今天的话题, 我上午有一个面谈, 我冥冥中就感觉会被问到 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. 只需简单5步,Ansible脚本自动搭建AlwaysOn集群(已测试通过,可实际运行)

    只需简单5步,Ansible脚本自动搭建AlwaysOn集群(已测试通过,可实际运行) 之前已经介绍过这套脚本,请看下面↓ 一分钟搞定!CentOS 7.9上用Ansible自动化部署SQL Serv ...

  2. ASP.NET Core 6 基础入门系列(1) ASP.NET Core 6.0 简介

    系列目录     [已更新最新开发文章,点击查看详细] 预备知识1:  C#5.C#6.C#7.C#8.C#9.C#10 预备知识2:  .NET 对比 .NET Framework 预备知识3:   ...

  3. 从SQL Server迁移到Mysql Mysql导入SQL Server的数据库

    mysql怎么导入 SQl Server的数据库, SQL Server导出的SQL文件Mysql无法识别 需要用到的软件 Navicat mysql workbanch SQL Server man ...

  4. 微信小程序实现分类菜单激活状态随列表滚动而自动切换效果详解

    这篇文章主要介绍了微信小程序分类菜单激活状态跟随列表滚动自动切换,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧 目录 view结构 ...

  5. Vue2/Vue3 项目生产环境开启 vue devtools 插件线上调试 vue 组件

    说到 vue 项目的调试工具,必然少不了 "vue devtools 插件",此插件就像"手术刀"一样,是开发环境下的一个利器,生产环境一般情况没办法使用. 要 ...

  6. 吐血整理!2025 最好用 AI 工具全汇总,别再瞎找了!

    在当下这个 AI 蓬勃发展的时代,各类 AI 工具如雨后春笋般涌现,让人眼花缭乱.无论是职场人士想要提升工作效率,还是创作者渴望激发灵感.优化内容,亦或是学生期望找到学习的得力助手,都在苦苦寻觅真正好 ...

  7. 【BUG】axios 长数字精度丢失问题

    问题原因 出现改问题是于javascript 整数范围问题 java 中 Long 类型 -2的63次方 - 2的63次方减去1 但是javascript整数范围确没有那么大,导致Long数字过大前端 ...

  8. nginx 配置go服务反向代理

    nginx 配置 详细请看Nginx 极简教程 server { listen 80; server_name localhost; #charset koi8-r; # nginx访问活动日志 ac ...

  9. PIL或Pillow学习1

    PIL( Python Imaging Library)是 Python 的第三方图像处理库,由于其功能丰富,API 简洁易用,因此深受好评. 自 2011 年以来,由于 PIL 库更新缓慢,目前仅支 ...

  10. vue实现不同用户权限的方法

    Vue 实现不同用户权限的方法 在项目中,实现不同用户的权限控制是常见的需求也是常见的功能模块,例如管理系统中不同角色(管理员.普通用户等)应有不同的访问权限,小程序.App等在不同角色登入的时候显示 ...