一道同分排名的SQL题

1 概述

遇到这样一道题:
(1) 有一张学生课程分数表,字段有:ID、名称、性别、科目、分数。(名称换为学号更能标识唯一学生,但名称好阅读,故这里先认为名称可以唯一标识学生。)
(2) 用一个SQL,分别统计每个学生多科目的总分,按性别输出前两名的学生和对应的总分。
这道题有两个难处理的点:
  • 在一个SQL里需要分两类处理:按男女性别。
  • 总分可能会同分,如果第一名有两个以上的人,那么需要输出多个人,就没有第二名了。如果第一名只有一个,第二名有多个,输出结果同样有多个,那么就没有第三及部分其它名次的人了。

2 解题

2.1 准备

先定义表结构,以MySQL 5.7为例:

-- DROP TABLE IF EXISTS`student_scores`;
CREATE TABLE IF NOT EXISTS `student_scores` (`id` BIGINT NOT NULL COMMENT 'id' AUTO_INCREMENT,`name` VARCHAR(64) COMMENT 'student name',`sex` VARCHAR(16) COMMENT 'student male or female',`subject` VARCHAR(64) COMMENT 'subject to learn',`score` INTEGER COMMENT 'score of subject',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='student scores';

2.2 查询SQL

这种SQL比较复杂,方法也不止一种,下面是一种较容易理解的:

SELECT a.name,a.sex,a.total_score
FROM (SELECT name,sex,SUM(score) AS total_scoreFROM student_scoresGROUP BY name, sex
) AS a
LEFT JOIN (SELECT name AS other_name,sex AS other_sex,SUM(score) AS other_totalFROM student_scoresGROUP BY name, sex
) AS b
ON a.sex = b.other_sex AND a.total_score < b.other_total
GROUP BY a.name, a.sex, a.total_score
HAVING COUNT(b.other_name) <= 1
ORDER BY a.sex DESC,a.total_score DESC;

分析:

(1) SQL里面第一个FROM后面的子查询和LEFT JOIN后面的子查询,结果是一样的,都是把每个学生的总分计算出来。只是为了容易区分,LEFT JOIN后面的子查询把结果的字段名重命名了一下。

SELECT name,               sex,              SUM(score) AS total_score FROM student_scores GROUP BY name, sex;
SELECT name AS other_name, sex AS other_sex, SUM(score) AS other_total FROM student_scores GROUP BY name, sex;

(2) 如果把上面子查询用一个临时表表示,SQL则可以简化:

CREATE TABLE IF NOT EXISTS `student_total_scores` (`id` BIGINT NOT NULL COMMENT 'id' AUTO_INCREMENT,`name` VARCHAR(64) COMMENT 'student name',`sex` VARCHAR(16) COMMENT 'student male or female',`total_score` INTEGER COMMENT 'score of subject',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='student total scores';-- 查询SQL简化为:
SELECT a.name,a.sex,a.total_score
FROM student_total_scores AS a
LEFT JOIN student_total_scores AS b
ON a.sex = b.other_sex AND a.total_score < b.other_total
GROUP BY a.name, a.sex, a.total_score
HAVING COUNT(b.other_name) <= 1
ORDER BY a.sex DESC,a.total_score DESC;
(3) 相当于一张表自关联查询,关联条件是:一要性别相同,二要主表的分数比关联表的分数要小。
这里要先理解LEFT JOIN语句的一些知识:
  • 如果对于主表的一行记录,在关联表里面若有多行满足要求的记录,此时结果记录会把主表的这条记录关联出和满足要求的记录一样多的记录。
  • 如果对于主表的一行记录,在关联表里面只有一行满足要求的记录、或者没有满足要求的记录,那么结果只有一条记录。

在这个基础上理解a.total_score < b.other_total条件,在同一个性别内,如果在关联表中有多行记录的分数是比当前行的分数高的,那么结果就有多行记录,这个行数N可以表示当前行分数排第(N+1)名。如果没有比当前行分数高的,那么当前行的分数就是第一名。注意,不管是否同分,只要分数高的就算一行。
对于HAVING COUNT(b.other_name) <= 1条件,当按name、sex、total_score进行group by之后,COUNT(b.other_name)实际就是计算关联出多少条记录,也就是比当前分数高的记录数。这个count为0代表第一名,为1代表第二名,为2代表第三名,以此类推。题目要求列前两名,所以这个count要小于等于1。
用例子理解一下:
  • 场景一:假设在一个性别内,A为第一名,B和C并列第二名,D第四名,此时A行记录联查不到记录、count=0,B和C都只能联查到A只有一行记录、count=1,D联查到ABC有三行记录、count=3,所以结果列出的是ABC。如果第二名并列更多人,它们的记录count仍然为1、符合输出条件,D的count则变得更大、不满足输出条件。
  • 场景二:假设在一个性别内,AB并列第一名,C并列第三名,D第四名,此时A和B行记录联查不到记录、count=0,C联查到A和B有两条记录、count=2,D联查到ABC有三行记录、count=3,所以结果列出的是AB。如果第一名并列更多人,它们的count仍然为0、符合输出条件,C和D的count会变大、不满足输出条件。
  • 场景三:假设在一个性别内,A为第一名,B为第二名,C为第三名,D第四名,此时A行记录联查不到记录、count=0,B行记录联查到一行记录、count=1,C行记录联查到两行记录、count=2,D行记录联查到3行记录、count=3。满足输出条件的为A和B。

2.3 测试

这里只列一下场景一“并列第二名”的测试,其它的可以修改一下数据同样验证:
insert into student_scores(`id`, `name`, `sex`, `subject`, `score`) values(1001, 'zhangsan', 'male', 'yuwen', 84);
insert into student_scores(`id`, `name`, `sex`, `subject`, `score`) values(1002, 'zhangsan', 'male', 'shuxue', 98);
insert into student_scores(`id`, `name`, `sex`, `subject`, `score`) values(1003, 'zhangsan', 'male', 'yingyu', 99);insert into student_scores(`id`, `name`, `sex`, `subject`, `score`) values(1006, 'lisi', 'female', 'yuwen', 90);
insert into student_scores(`id`, `name`, `sex`, `subject`, `score`) values(1007, 'lisi', 'female', 'shuxue', 66);
insert into student_scores(`id`, `name`, `sex`, `subject`, `score`) values(1008, 'lisi', 'female', 'yingyu', 89);insert into student_scores(`id`, `name`, `sex`, `subject`, `score`) values(1011, 'wangwu', 'female', 'yuwen', 77);
insert into student_scores(`id`, `name`, `sex`, `subject`, `score`) values(1012, 'wangwu', 'female', 'shuxue', 69);
insert into student_scores(`id`, `name`, `sex`, `subject`, `score`) values(1013, 'wangwu', 'female', 'yingyu', 93);insert into student_scores(`id`, `name`, `sex`, `subject`, `score`) values(1016, 'chenliu', 'male', 'yuwen', 95);
insert into student_scores(`id`, `name`, `sex`, `subject`, `score`) values(1017, 'chenliu', 'male', 'shuxue', 99);
insert into student_scores(`id`, `name`, `sex`, `subject`, `score`) values(1018, 'chenliu', 'male', 'yingyu', 88);insert into student_scores(`id`, `name`, `sex`, `subject`, `score`) values(1021, 'huangqi', 'male', 'yuwen', 82);
insert into student_scores(`id`, `name`, `sex`, `subject`, `score`) values(1022, 'huangqi', 'male', 'shuxue', 100);
insert into student_scores(`id`, `name`, `sex`, `subject`, `score`) values(1023, 'huangqi', 'male', 'yingyu', 99);insert into student_scores(`id`, `name`, `sex`, `subject`, `score`) values(1026, 'liuba', 'female', 'yuwen', 85);
insert into student_scores(`id`, `name`, `sex`, `subject`, `score`) values(1027, 'liuba', 'female', 'shuxue', 79);
insert into student_scores(`id`, `name`, `sex`, `subject`, `score`) values(1028, 'liuba', 'female', 'yingyu', 66);insert into student_scores(`id`, `name`, `sex`, `subject`, `score`) values(1031, 'zhaojiu', 'male', 'yuwen', 90);
insert into student_scores(`id`, `name`, `sex`, `subject`, `score`) values(1032, 'zhaojiu', 'male', 'shuxue', 92);
insert into student_scores(`id`, `name`, `sex`, `subject`, `score`) values(1033, 'zhaojiu', 'male', 'yingyu', 80);-- 表记录结果:
+------+----------+--------+---------+-------+
| id   | name     | sex    | subject | score |
+------+----------+--------+---------+-------+
| 1001 | zhangsan | male   | yuwen   |    84 |
| 1002 | zhangsan | male   | shuxue  |    98 |
| 1003 | zhangsan | male   | yingyu  |    99 |
| 1016 | chenliu  | male   | yuwen   |    95 |
| 1017 | chenliu  | male   | shuxue  |    99 |
| 1018 | chenliu  | male   | yingyu  |    88 |
| 1021 | huangqi  | male   | yuwen   |    82 |
| 1022 | huangqi  | male   | shuxue  |   100 |
| 1023 | huangqi  | male   | yingyu  |    99 |
| 1031 | zhaojiu  | male   | yuwen   |    90 |
| 1032 | zhaojiu  | male   | shuxue  |    92 |
| 1033 | zhaojiu  | male   | yingyu  |    80 |
| 1006 | lisi     | female | yuwen   |    90 |
| 1007 | lisi     | female | shuxue  |    66 |
| 1008 | lisi     | female | yingyu  |    89 |
| 1011 | wangwu   | female | yuwen   |    77 |
| 1012 | wangwu   | female | shuxue  |    69 |
| 1013 | wangwu   | female | yingyu  |    93 |
| 1026 | liuba    | female | yuwen   |    85 |
| 1027 | liuba    | female | shuxue  |    79 |
| 1028 | liuba    | female | yingyu  |    66 |
+------+----------+--------+---------+-------+

看一下总分的结果:

+----------+--------+-------------+
| name     | sex    | total_score |
+----------+--------+-------------+
| huangqi  | male   |         281 |
| zhangsan | male   |         281 |
| chenliu  | male   |         282 |
| zhaojiu  | male   |         262 |
| lisi     | female |         245 |
| wangwu   | female |         239 |
| liuba    | female |         230 |
+----------+--------+-------------+

对于sex=male的数据,第一名为282分,第二名281分并列有两个,第四名为262分。sex=female的数据,则前三名各一个。

完整SQL输出的结果:

+----------+--------+-------------+
| name     | sex    | total_score |
+----------+--------+-------------+
| chenliu  | male   |         282 |
| huangqi  | male   |         281 |
| zhangsan | male   |         281 |
| lisi     | female |         245 |
| wangwu   | female |         239 |
+----------+--------+-------------+

3 小结

这种复杂的SQL是性能比较低的,且非常难维护和扩展,在工作中基本是禁止使用的。只是有些同事是SQL流的,总能写出一些很复杂的SQL,给后面的维护带来很大的不便,看不懂就改不了,好不容易看懂了也不太敢改。

这种SQL用来考试可能有点考智力的意思,即使懂JOIN、GROUP BY、HAVING、ORDER BY语句的知识,还得能够想出把排名转化为记录数才行。在工作中最好不用,用代码逻辑或者更好的设计来代替。

此外,现在AI工具比较多,问AI可以得到更多答案,每个AI都可能给出一个不同的方式、还不一定是对的,想挑战一下SQL知识的可以都试试。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/93663.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/93663.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ICCV 2025 | Reverse Convolution and Its Applications to Image Restoration

标题&#xff1a;Reverse Convolution and Its Applications to Image Restoration作者&#xff1a;Xuhong Huang, Shiqi Liu, Kai Zhang, Ying Tai, Jian Yang, Hui Zeng, Lei Zhang单位&#xff1a;Nanjing University, The Hong Kong Polytechnic University, OPPO Research…

mysql启动超时

mysql启动超时&#xff1a; 管理员打开CMD后允许net start MySQL57&#xff0c; 启动超时检查错误日志 MySQL 启动失败的具体原因通常记录在错误日志中。 日志路径&#xff08;根据你的安装方式可能不同&#xff09;&#xff1a; 默认位置&#xff1a;C:\ProgramData\MySQL\MyS…

Flink Stream API 源码走读 - window 和 sum

本文核心观点 核心观点&#xff1a;WindowedStream 是一个"假流"&#xff0c;它比 KeyedStream 更虚&#xff0c;只是一个 API 的过渡器&#xff0c;不是真正意义上的 DataStream&#xff0c;需要调用函数回归。 虚拟化时刻&#xff1a;从真实流到虚拟流 KeyedStream…

蓝牙 GFSK RX Core 架构解析

GFSK RX Core分为以下几个模块&#xff1a; 1.Frequency offset compensation CORDIC 2.A low pass filter 3.A power estimator for packet detection,RSSI and digital gaion computation for DPSK path 4.A demodulator implemented as Phase Shift Discriminator 5.A drequ…

微电网管控系统中python多线程缓存与SQLite多数据库文件连接池实践总结(含源码)

1. 引言 在分散的微电网能源管理场景中,系统采用集中式云平台模式,为100个独立微电网用户提供高并发数据写入服务面临三大挑战:用户数据隔离、I/O性能瓶颈、多线程安全性。本文揭示一种新式的分片锁+三级缓存+sqlite多数据库文件连接池架构,在保持SQLite轻量级优势的同时,…

InfluxDB 开发工具链:IDE 插件与调试技巧(一)

引言 ** 在当今数字化时代&#xff0c;时间序列数据的处理与分析在众多领域中都扮演着至关重要的角色。无论是物联网设备产生的海量传感器数据&#xff0c;还是金融市场中实时波动的交易数据&#xff0c;又或是服务器运维过程中不断产生的性能指标数据&#xff0c;这些都属于…

计算机网络-IPv6

1、IPv6基础IPv4与IPv6的对比&#xff1a;问题IPv4的缺陷IPv6的优势地址空间IPv4地址采用32比特标识&#xff0c;能提供的地址数量是43亿&#xff0c;分配很不均衡。针对IPv4的地址短缺问题&#xff0c;有几种解决方案&#xff1a;无类别域间路由CIDR&#xff08;Classless Int…

整体设计 之“凝聚式中心点”原型 --整除:智能合约和DBMS的深层融合 之2

摘要&#xff08;CSDN的AI助手自动生成的&#xff09;本文提出了一种基于"整除"数学原型的智能合约与DBMS融合架构设计&#xff0c;将SQL查询语句的四个关键段&#xff08;SELECT、FROM、WHERE、BY&#xff09;分别映射到整除运算的四个要素&#xff08;商、被除数、…

【赵渝强老师】TiDB表数据与键值对的映射关系

TiDB实例将表中的每一行数据映射成RocksDB中的键值对&#xff0c;则需要考虑如何构造Key和Value。首先&#xff0c;OLTP场景下有大量针对单行或者多行的增、删、改、查等操作&#xff0c;要求数据库具备快速读取一行数据的能力。因此&#xff0c;对应的Key最好有一个唯一ID&…

带操作系统的延时函数

delay.c:#include "delay.h"/*** brief 微秒级延时* param nus 延时时长&#xff0c;范围&#xff1a;0~233015* retval 无*/ void delay_us(uint32_t nus) {uint32_t ticks;uint32_t tcnt 0, told, tnow;uint32_t reload SysTick->LOAD; //重…

ES Module 和 CommonJS的区别

ES Module&#xff08;ESM&#xff0c;ES6 模块系统&#xff09;和 CommonJS 是 JavaScript 中两种主流的模块规范&#xff0c;分别用于现代前端和 Node.js 环境&#xff08;早期&#xff09;&#xff0c;它们在语法、加载机制、特性等方面有显著区别。以下是详细对比&#xff…

猫头虎AI分享|一款智能量化交易系统:QuantCell,从数据收集到策略执行全流程自动化

猫头虎AI分享&#xff5c;一款智能量化交易系统&#xff1a;QuantCell&#xff0c;从数据收集到策略执行全流程自动化 在当今金融市场中&#xff0c;量化交易系统已经成为越来越多投资者和机构的重要选择。无论是股票、期货还是加密货币&#xff0c;自动化交易与人工智能的结合…

直播美颜SDK架构揭秘:动态贴纸功能的实现原理与性能优化

如今&#xff0c;美颜SDK 已经不再只是“磨皮、美白”的基础工具&#xff0c;而是逐渐进化为一个涵盖 人脸识别、实时特效、动态贴纸交互 的复杂技术体系。尤其是 动态贴纸功能 的加入&#xff0c;让主播与观众之间的互动更加生动有趣&#xff0c;也成为提升用户粘性与平台差异…

Docker安装CDC

Docker安装CDC拉取镜像离线形式安装上传文件并创建docker-compose.yml把镜像加载到docker中启动容器连接数据库创建账号&#xff0c;并给账号授权设置wal_level确认wal_level的值创建链接查询连接状态使用kafdrop消息中看不到修改之前的信息怎么办补充拉取镜像 docker pull co…

如何在win服务器中部署若依项目

一、安装jdk的环境&#xff1a; 这一步很简单&#xff0c;直接拿到安装包双击安装即可。 二、配置jdk的环境变量默认安装的路径为&#xff1a;C:\Program Files (x86)\Java\jdk1.7.0_51安装完成之后进行环境变量配置右击计算机&#xff08;此电脑&#xff09;点击属性点击高级系…

CSS从入门到精通完整指南

第一部分&#xff1a;CSS基础入门1.1 什么是CSSCSS&#xff08;层叠样式表&#xff0c;Cascading Style Sheets&#xff09;是用于描述HTML文档外观和格式的样式语言。CSS将内容与表现分离&#xff0c;让HTML专注于内容结构&#xff0c;CSS专注于视觉效果。1.2 CSS语法结构选择…

重温k8s基础概念知识系列二(Pod)

文章目录1、Pod概念2、K8s 中的 Pod 的两种用法3、定义Pod4、Pod的创建资源5、Pod 模板6、容器探针7、总结干货8、 K8s Pod 经典面试题速查表Pod是Kubernetes中最小的单元&#xff1a; 1、Pod概念 Pod 是可以在 Kubernetes中创建和管理的、最小的可部署的计算单元。它由一组、一…

设计模式之静态代理

一些个人理解 顾名思义&#xff0c;就是代理一个对象。 那么&#xff0c;既然要代理一个东西&#xff0c;就要传入它吧? 【1】所以将代理对象当作属性【【2】往往通过构造方法传入被代理的目标对象】。 既然要代理&#xff0c;那必然要和代理对象拥有相同的功能吧? 所以实现了…

牛津大学xDeepMind 自然语言处理(1)

牛津大学xDeepMind 自然语言处理 Natural Language Processing 词向量与词汇语义学 Word Vectors and Lexical Semantics 词语表示的基本问题与分布语义思想 传统词语表示&#xff08;如独热向量&#xff09;存在稀疏、正交、语义弱的问题&#xff0c;无法表达语义相似性。分布…

StarRocks数据库集群的完整部署流程

目录 依赖环境 下载安装包 部署FE 部署BE 搭建集群 停止集群 依赖环境 详见&#xff1a;StarRocks 部署&#xff1a;依赖环境-CSDN博客 下载安装包 在官方网站下载安装包&#xff1a;StarRocks 部署FE 创建元数据目录。 mkdir -p <meta_dir> 修改 FE 配置文件 f…