SQL 多表关联与分组聚合:解密答题正确率分析
一、问题拆解:从业务需求到SQL逻辑
1.1 需求分析
题目要求:计算浙江大学用户在不同难度题目下的答题正确率,并按正确率升序排序。
关键分析点:
- 数据来源:
user_profile
:存储用户信息(大学)question_practice_detail
:存储答题记录question_detail
:存储题目难度信息
- 筛选条件:
university = '浙江大学'
- 计算逻辑:
- 正确率 = 正确答题数 / 总答题数
- 需按题目难度分组(
difficult_level
)
- 结果要求:按正确率升序排列
1.2 示例数据与预期结果
假设各表结构及部分数据:
user_profile:
device_id | university |
---|---|
1001 | 浙江大学 |
1002 | 浙江大学 |
1003 | 复旦大学 |
question_practice_detail:
device_id | question_id | result |
---|---|---|
1001 | Q101 | right |
1001 | Q102 | wrong |
1002 | Q102 | right |
1002 | Q103 | right |
question_detail:
question_id | difficult_level |
---|---|
Q101 | hard |
Q102 | medium |
Q103 | easy |
预期结果:
difficult_level | correct_rate |
---|---|
medium | 0.5000 |
hard | 1.0000 |
easy | 1.0000 |
二、核心SQL解析:多表关联与聚合计算
2.1 完整SQL语句
SELECT qd.difficult_level, ROUND(SUM(CASE WHEN qpd.result = 'right' THEN 1 ELSE 0 END) / COUNT(qpd.question_id), 4) AS correct_rate
FROM user_profile AS up
INNER JOIN question_practice_detail AS qpd ON up.device_id = qpd.device_id
INNER JOIN question_detail AS qd ON qpd.question_id = qd.question_id
WHERE up.university = '浙江大学'
GROUP BY qd.difficult_level
ORDER BY correct_rate ASC;
2.2 关键技术点分解
1. 三表关联策略
user_profile INNER JOIN question_practice_detail INNER JOIN question_detail
- 作用:
- 通过
device_id
关联用户与答题记录 - 通过
question_id
关联答题记录与题目难度
- 通过
- 选择内连接的原因:
- 仅统计实际答题的用户和题目
- 排除未答题用户或无难度信息的题目
2. 正确率计算逻辑
ROUND(SUM(CASE WHEN qpd.result = 'right' THEN 1 ELSE 0 END) / COUNT(qpd.question_id), 4)
- 分子:
SUM(CASE ...)
统计正确答题数 - 分母:
COUNT(qpd.question_id)
统计总答题数 - ROUND函数:保留四位小数
3. 分组与排序
GROUP BY qd.difficult_level
ORDER BY correct_rate ASC
- 分组:按题目难度(
difficult_level
)分组 - 排序:按计算出的正确率升序排列
三、SQL语法精讲
3.1 多表连接语法
table1 [INNER|LEFT|RIGHT] JOIN table2 ON condition1
[INNER|LEFT|RIGHT] JOIN table3 ON condition2
- 本例连接类型:内连接(INNER JOIN)
- 执行顺序:从左到右依次连接各表
3.2 聚合函数详解
函数 | 作用 |
---|---|
SUM(expr) | 计算表达式的总和 |
COUNT(expr) | 统计非NULL值的数量 |
ROUND(num, dec) | 将数值四舍五入到指定小数位数 |
3.3 CASE表达式
CASE WHEN condition THEN value ELSE default_value
END
- 本例应用:将布尔条件(
result='right'
)转换为数值(1或0)
四、执行流程与数据流转
4.1 分步执行过程
步骤1:筛选浙江大学用户
WHERE up.university = '浙江大学'
- 结果:保留
user_profile
中浙江大学的用户记录
步骤2:连接答题记录表
INNER JOIN question_practice_detail AS qpd
ON up.device_id = qpd.device_id
- 结果:
device_id university question_id result 1001 浙江大学 Q101 right 1001 浙江大学 Q102 wrong 1002 浙江大学 Q102 right 1002 浙江大学 Q103 right
步骤3:连接题目难度表
INNER JOIN question_detail AS qd
ON qpd.question_id = qd.question_id
- 结果:
device_id university question_id result difficult_level 1001 浙江大学 Q101 right hard 1001 浙江大学 Q102 wrong medium 1002 浙江大学 Q102 right medium 1002 浙江大学 Q103 right easy
步骤4:分组与聚合计算
- 按难度分组:
hard
:Q101(1条记录,1正确)medium
:Q102(2条记录,1正确)easy
:Q103(1条记录,1正确)
- 计算正确率:
hard
:1/1 = 1.0000medium
:1/2 = 0.5000easy
:1/1 = 1.0000
步骤5:排序
ORDER BY correct_rate ASC
- 最终结果:按正确率升序排列
五、性能优化策略
5.1 索引优化
-- 为user_profile添加索引
CREATE INDEX idx_university_device ON user_profile(university, device_id);-- 为question_practice_detail添加复合索引
CREATE INDEX idx_device_question ON question_practice_detail(device_id, question_id, result);-- 为question_detail添加索引
CREATE INDEX idx_question_difficulty ON question_detail(question_id, difficult_level);
5.2 执行计划分析
使用EXPLAIN
关键字分析执行计划:
EXPLAIN
SELECT ... (原SQL) ...;
关键指标解读:
type
列:期望各表连接类型为ref
或eq_ref
key
列:应显示使用了上述创建的索引Extra
列:避免出现Using filesort
和Using temporary
六、常见问题与解决方案
6.1 除数为零问题
问题:若某难度题目无人作答,COUNT(qpd.question_id)
为0,导致除零错误。
解决方案:
-- 使用NULLIF防止除零
ROUND(SUM(CASE WHEN qpd.result = 'right' THEN 1 ELSE 0 END) / NULLIF(COUNT(qpd.question_id), 0), 4)-- 若存在无答题记录的难度,需改用LEFT JOIN并处理NULL
6.2 正确率精度问题
问题:直接相除可能导致精度丢失。
解决方案:
-- 显式转换为DECIMAL类型
ROUND(SUM(CASE WHEN qpd.result = 'right' THEN 1 ELSE 0 END) / COUNT(qpd.question_id), 4) AS correct_rate
6.3 多表连接性能问题
问题:大数据量下多表连接可能性能较差。
解决方案:
- 确保连接字段都有索引
- 优先过滤数据(如先筛选浙江大学用户)
- 考虑使用临时表存储中间结果
七、扩展应用:分组聚合的进阶场景
7.1 计算各大学平均正确率
SELECT up.university,ROUND(SUM(CASE WHEN qpd.result = 'right' THEN 1 ELSE 0 END) / COUNT(qpd.question_id), 4) AS avg_correct_rate
FROM ...
GROUP BY up.university
ORDER BY avg_correct_rate DESC;
7.2 按难度和用户分组
SELECT up.device_id,qd.difficult_level,ROUND(SUM(CASE WHEN qpd.result = 'right' THEN 1 ELSE 0 END) / COUNT(qpd.question_id), 4) AS user_difficulty_rate
FROM ...
GROUP BY up.device_id, qd.difficult_level;
7.3 复杂条件筛选
WHERE up.university IN ('浙江大学', '复旦大学')AND qd.difficult_level IN ('hard', 'medium')AND qpd.date >= '2021-08-01';
八、总结与技术要点
8.1 核心技术点回顾
- 多表关联策略:通过内连接整合三张表的数据
- 分组聚合技巧:
- 使用
SUM(CASE ...)
统计条件计数 - 使用
COUNT
统计总数 - 使用
ROUND
控制结果精度
- 使用
- 排序与过滤:合理使用
WHERE
、GROUP BY
和ORDER BY
- 索引优化:为连接字段和过滤条件创建复合索引
8.2 技术决策树
开始
│
├── 是否需要多表关联?
│ │
│ └── 是 → 选择合适的连接类型(INNER/LEFT)
│ │
│ ├── 仅保留匹配记录 → INNER JOIN
│ │
│ └── 保留主表所有记录 → LEFT JOIN
│
├── 是否需要分组统计?
│ │
│ └── 是 → 确定分组字段和聚合函数
│ │
│ ├── 统计总数 → COUNT
│ │
│ ├── 统计条件数 → SUM(CASE ...)
│ │
│ └── 计算比例 → SUM(...)/COUNT(...)
│
├── 是否存在性能问题?
│ │
│ └── 是 → 为JOIN字段和WHERE条件创建复合索引
│
└── 结束
通过深入理解多表关联和分组聚合的原理,结合合理的索引优化,你可以高效处理各种复杂的统计分析需求,避免常见的SQL性能陷阱,提升数据处理能力。
相关文章:
SQL 多表关联与分组聚合:解密答题正确率分析
一、问题拆解:从业务需求到SQL逻辑 1.1 需求分析 题目要求:计算浙江大学用户在不同难度题目下的答题正确率,并按正确率升序排序。 关键分析点: 数据来源: user_profile:存储用户信息(大学&a…...

基于 Redis 实现短信验证码登录功能的完整方案
🧱 一、技术栈与依赖配置 使用 Spring Boot Redis 实现短信验证码登录,以下是推荐的 Maven 依赖: <dependencies><!-- Spring Boot Web --><dependency><groupId>org.springframework.boot</groupId><ar…...

电平匹配电路
1、为什么要电平匹配? 现在很多SOC器件为了降低功耗,都把IO口的电平设计成了1.8V,核电压0.85V,当这种SOC做主平台时,在做接口设计需要格外关注电平的匹配。单板中经常需要将1.8V的电平转换成3.3V或者转成5V。如果没有注意到输入和输出信号之间的电平匹配,系统就无法正常…...

JavaScript 日志和调试工具箱-logger2js
原创功能丰富的 JavaScript 日志和调试工具箱,设计这个工具时考虑到了多种实际开发中的需求。该工具不仅提供了高效强大的日志输出显示功能,还包含了界面风格配置、代码格式化、事件处理、性能测试、方法调用栈输出,右键菜单、控制台显示控制…...
GitHub 自动认证教程
## 简介 在使用 GitHub 时,为了避免每次提交代码都需要输入用户名和密码,我们可以使用 SSH 密钥进行自动认证。本教程将详细介绍如何设置 SSH 密钥并配置 GitHub 自动认证。 ## 步骤一:检查现有 SSH 密钥 首先,检查您的电脑是否…...

zData X zStorage 为什么采用全闪存架构而非混闪架构?
点击蓝字 关注我们 最近有用户问到 zData X 的存储底座 zStorage 分布式存储为什么采用的是全闪存架构而非混闪架构?主要原因还是在于全闪存架构在性能和可靠性方面具有更显著的优势。zData X 的上一代产品 zData 的早期版本也使用了SSD盘作为缓存的技术架构&#x…...
鸿蒙OSUniApp 实现精美的轮播图组件#三方框架 #Uniapp
UniApp 实现精美的轮播图组件 在移动应用开发中,轮播图是一个非常常见且重要的UI组件。本文将深入探讨如何使用UniApp框架开发一个功能丰富、动画流畅的轮播图组件,并分享一些实际开发中的经验和技巧。 一、基础轮播图实现 1.1 组件结构设计 首先&am…...
解决git中断显示中文为八进制编码问题
git config --global core.quotepath false 命令用于配置 Git 如何处理非 ASCII 字符(如中文、日文、韩文等)的文件名显示 core.quotepath Git 的一个核心配置项,控制是否对非 ASCII 文件名进行转义(quote)处理。 f…...
SQL次日留存率计算精讲:自连接与多字段去重的深度应用
一、问题拆解:理解次日留存率的计算逻辑 1.1 业务需求转换 题目:运营希望查看用户在某天刷题后第二天还会再来刷题的留存率。 关键分析点: 留存率 (第一天刷题且第二天再次刷题的用户数) / 第一天刷题的总用户数需…...

使用SQLite Studio导出/导入SQL修复损坏的数据库
使用SQLite Studio导出/导入SQL修复损坏的数据库 使用Zotero时遇到了数据库损坏,在软件中寸步难行,遂尝试修复数据库。 一、SQLite Studio简介 SQLite Studio是一款专为SQLite数据库设计的免费开源工具,支持Windows/macOS/Linux。相较于其…...
LSTM-Attention混合模型:美债危机与黄金对冲效率研究
摘要:本文依托多维度量化分析框架,结合自然语言处理(NLP)技术对地缘文本的情绪挖掘,构建包含宏观因子、风险溢价因子及技术面因子的三阶定价模型,对当前黄金市场的波动特征进行归因分析。实证结果显示&…...
了解 DDD 吗?DDD 和 MVC 的区别是什么?
简介: DDD(Domain-driven Design) 和 MVC(Model-View-Controller) 是软件后台开发两种流行的分层架构思想。 MVC 是一种设计模式,主要用来分离用户界面,业务逻辑,和数据模型。 而…...

Unity3D仿星露谷物语开发46之种植/砍伐橡树
1、目标 种植一棵橡树,从种子变成大树。 然后可以使用斧头砍伐橡树。 2、删除totalGrowthDays字段 修改growthDays的含义,定义每个值为到达当前阶段的累加天数。此时最后一个阶段就是totalGrowthDays的含义。所以就可以删除totalGrowthDays字段。 &…...
STM32外设应用详解——从基础到高级应用的全面指南
目录 一、引言:为何选择STM32外设 二、主要外设类别与详细应用解析 1. GPIO(通用输入输出) 工作原理详解 高级应用设计 硬件连接建议 2. 定时器(TIM)详解 基本定时器原理 高级配置 实际应用 核心技巧 3. A…...
作业帮C++后台开发面试题及参考答案
Cookie 和 Session 的区别是什么? Cookie 和 Session 是 Web 开发中用于管理用户状态的两种机制,它们在存储位置、安全性、生命周期和数据类型等方面存在显著差异。 存储位置:Cookie 数据存储在客户端浏览器,而 Session 数据存储在服务器端。当浏览器向服务器发送请求时,…...
红队进阶实战
4.1 内网渗透(域渗透、横向移动) 域环境攻击链 初始立足点:通过钓鱼获取域用户凭据(如NTLM Hash)。信息收集: 使用BloodHound自动化分析域内关系。执行nltest /dclist:domain.com获取域控制器列表。横向移动: Pass-the-Hash:利用Mimikatz注入Hash到新会话。sekurlsa::…...
C语言中的指定初始化器
什么是指定初始化器? C99标准引入了一种更灵活、直观的初始化语法——指定初始化器(designated initializer), 可以在初始化列表中直接引用结构体或联合体成员名称的语法。通过这种方式,我们可以跳过某些不需要初始化的成员,并且可以以任意顺序对特定成员进行初始化。这…...
C/C++ 整数类型的长度
参考 cppreference.cn 在某些语言中,整数类型的长度是固定的,如java中 char 8short 16int 32long 64 可是C/C 与机器相关,整数类型长度与平台有关 先可以记一个简单的 按照C标准: char > 8short > 16int > 16long &g…...

gRPC开发指南:Visual Studio 2022 + Vcpkg + Windows全流程配置
前言 gRPC作为Google开源的高性能RPC框架,在微服务架构中扮演着重要角色。本文将详细介绍在Windows平台下,使用Visual Studio 2022和Vcpkg进行gRPC开发的完整流程,包括环境配置、项目搭建、常见问题解决等实用内容。 环境准备 1. 安装必要组…...

高密度服务器机柜散热方案:高风压风机在复杂风道中的关键作用与选型要点
随着云计算、人工智能等技术的飞速发展,数据中心内服务器机柜的集成度不断攀升,高密度部署成为常态。然而,高密度意味着单位空间内服务器数量剧增,发热量呈指数级上升,传统散热方案已难以满足需求。在复杂的机柜风道环…...
Android framework 问题记录
一、休眠唤醒,很快熄屏 1.1 问题描述 机器休眠唤醒后,没有按照约定的熄屏timeout 进行熄屏,很快就熄屏(约2s~3s左右) 1.2 原因分析: 抓取相关log,打印休眠背光 相关调用栈 //具体打印调用栈…...

框架之下再看HTTP请求对接后端method
在当今的软件开发中,各类框架如雨后春笋般不断涌现,极大地提升了开发效率。以 Java 开发为例,Spring 框架历经多次迭代演进,而 Spring Boot 更是将开发便捷性提升到了新高度。如今,开发者只需简单引入 Maven 包&#x…...
Oracle APEX IR报表列宽调整
目录 1. 问题:如何调整Oracle APEX IR报表列宽 2. 解决办法 1. 问题:如何调整Oracle APEX IR报表列宽 1-1. 防止因标题长而数据短,导致标题行的文字都立起来了,不好看。 1-2. 防止因数据太长而且中间还没有空格,把列…...

【笔记】与PyCharm官方沟通解决开发环境问题
#工作记录 2025年5月20日 星期二 背景 在此前的笔记中,我们提到了向PyCharm官方反馈了几个关于Conda环境自动激活、远程解释器在社区版中的同步问题以及Shell脚本执行时遇到的问题。这些问题对日常开发流程产生了一定影响,因此决定联系官方支持寻求解…...
深入解析:如何基于开源OpENer开发EtherNet/IP从站服务
一、EtherNet/IP协议概述 EtherNet/IP(Industrial Protocol)是一种基于以太网的工业自动化通信协议,它将CIP(Common Industrial Protocol)封装在标准以太网帧中,通过TCP/IP和UDP/IP实现工业设备间的通信。作为ODVA(Open DeviceNet Vendors Association)组织的核心协议…...

node.js文件系统(fs) - 创建文件、打开文件、写入数据、追加数据、读取数据、创建目录、删除目录
注意:以下所有示例均是异步语法! 注意:以下所有示例均是异步语法! 创建文件 node.js 允许我们在计算机本地创建文件,例如创建一个 word 文件: // 引入核心模块(fs) var fs require(fs)// API fs.writeF…...
SQL:MySQL函数:空值处理函数(NULL Handling Functions)
目录 什么是空值(NULL)? 常用空值处理函数总览 1️⃣ IFNULL() – 空值替换函数(If Null) 2️⃣ COALESCE() – 多参数空值判断(返回第一个非 NULL 值) 3️⃣ NULLIF() – 相等则返回 NULL…...

利用ffmpeg截图和生成gif
从视频中截取指定数量的图片 ffmpeg -i input.mp4 -ss 00:00:10 -vframes 1 output.jpgffmpeg -i input.mp4 -ss 00:00:10 -vframes 180 output.jpg -vframes 180代表截取180帧, 实测后发现如果视频是60fps,那么会从第10秒截取到第13秒-i input.mp4:指定输入视频文…...

初始化一个Springboot项目
初始化一个Springboot项目 文章目录 初始化一个Springboot项目1、新建项目2、配置yml3、自定义异常4、通用相应类5、全局跨域配置6、总结 1、新建项目 首先,我们需要创建一个新的 Spring Boot 项目。这里我们使用 IntelliJ IDEA 作为开发工具,它提供了方…...

YOLOv8在单目向下多车辆目标检测中的应用
大家读完觉得我有帮助记得关注!!! 摘要 自动驾驶技术正逐步改变传统的汽车驾驶方式,标志着现代交通运输的一个重要里程碑。目标检测是自主系统的基石,在提高驾驶安全性、实现自主功能、提高交通效率和促进有效的应急…...