SQL 强化练习 (二)
继续 sql 搞起来, 面向过程来弄, 重点是分析的思路, 涉及的的 left join, inner join, group by +_ having, case when ... 等场景, 也是比较详细地记录整个分析的过程, 虽然实现并不难.
数据关系
这个是都次都要重复提及的. 只有熟练知晓表结构, 才能做各种查询呀.

需求
第 02 题
查询平均成绩大于80分的学生的学号和平均成绩.
分析
只用使用一个 score 表就搞定了.
mysql> select * from 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)
直接对 score 表 对 s_id 做 group by 然后对 avg(score), 再组内过滤 (haing) 即可.
-- 先弄分组聚合, 注意要先写 s_id
select
s_id,
avg(score)
from score
group by
s_id;
+------+------------+
| s_id | avg(score) |
+------+------------+
| 0001 | 89.6667 |
| 0002 | 70.0000 |
| 0003 | 80.0000 |
+------+------------+
3 rows in set (0.00 sec)
这样, 学号和平均成绩都有了, 然后还要一个, 平均成绩大于 80, 这就是 对 group by 后的一个筛选, 用 having.
select
s_id as "学号",
avg(score) as "平均成绩"
from score
group by s_id
having avg(score) > 80;
这上面没过滤之前就能看出, 大于80的, 就只有1号老铁这个学霸.
+--------+--------------+
| 学号 | 平均成绩 |
+--------+--------------+
| 0001 | 89.6667 |
+--------+--------------+
1 row in set (0.00 sec)
补充
-- 增加对 group by 中没有用到的字段是没啥意义的
select
s_id as "学号",
avg(score) as "平均成绩",
c_id -- add
from score
group by s_id
having avg(score) > 80;
+--------+--------------+------+
| 学号 | 平均成绩 | c_id |
+--------+--------------+------+
| 0001 | 89.6667 | 0001 |
+--------+--------------+------+
1 row in set (0.00 sec)
这个 c_id 是没有任何意义的, 因为是平均成绩嘛, 因此, **最好不要将 group by 无关的字段给放在 select 中去哦.
其实 pandas 也是一样的处理方式, df.groupby("s_id").agg({"score": "mean"}) . 当然可能是习惯问题, 我总感觉 Python 这种写法会更加直观些, 先分组, 再聚合 , 说的是写法上哈.
本题的核心点: group by + having.
第 03 题
查询所有学生的学号, 姓名, 选课数, 总成绩.
分析
分析: 其实就是以学生表, 我主表, 然后左连接成绩表, 然后再按 s_id group by 再聚合即可.
首先用 学生表为主表, left join 成绩表即可.
select *
from student as a
left join score as b
on a.s_id = b.s_id;
先连接上看看.
+------+-----------+------------+--------+------+------+-------+
| s_id | s_name | birth_date | gender | s_id | c_id | score |
+------+-----------+------------+--------+------+------+-------+
| 0001 | 王二 | 1989-01-01 | 男 | 0001 | 0001 | 80 |
| 0001 | 王二 | 1989-01-01 | 男 | 0001 | 0002 | 90 |
| 0001 | 王二 | 1989-01-01 | 男 | 0001 | 0003 | 99 |
| 0002 | 星落 | 1990-12-21 | 女 | 0002 | 0002 | 60 |
| 0002 | 星落 | 1990-12-21 | 女 | 0002 | 0003 | 80 |
| 0003 | 胡小适 | 1991-12-21 | 男 | 0003 | 0001 | 80 |
| 0003 | 胡小适 | 1991-12-21 | 男 | 0003 | 0002 | 80 |
| 0003 | 胡小适 | 1991-12-21 | 男 | 0003 | 0003 | 80 |
| 0004 | 油哥 | 1996-10-01 | 男 | NULL | NULL | NULL |
+------+-----------+------------+--------+------+------+-------+
9 rows in set (0.06 sec)
然后, 对 s_id 进行 group by, 对 c_id 进行 count, 对 score 进行 sum.
select
a.s_id as "学号",
a.s_name as "姓名",
count(b.c_id) as "选课数",
sum(b.score) as "总成绩"
from student as a
left join score as b
on a.s_id = b.s_id
group by a.s_id;
阅读顺序跟写的顺序是, 先写表连接的部分, 然后再 group by, 最后再 写最终要查询的字段 (包含聚合)
+--------+-----------+-----------+-----------+
| 学号 | 姓名 | 选课数 | 总成绩 |
+--------+-----------+-----------+-----------+
| 0001 | 王二 | 3 | 269 |
| 0002 | 星落 | 2 | 140 |
| 0003 | 胡小适 | 3 | 240 |
| 0004 | 油哥 | 0 | NULL |
+--------+-----------+-----------+-----------+
4 rows in set (0.04 sec)
在这里看上去是没啥问题的, 对于这句 a.s_id as "学号", a.s_name as "姓名", 在本例, 学号跟姓名是 1:1 的关系, 是ok 的. 但假如说, 不是 1: 1 就可能产生很大的问题. 当然我用 pandas 时写也是这样类似的:
# 假设这里的 df 已经是 merge 好了的哈, 直接group by
df.groupby("s_id").agg({"s_id":" count"},
{"score": "sum"}
)
其实套路都是一样的, 重点是理解这个 , apply -> group by -> aggregation 的通用套路. 就连大数据的 mapReduce 也是差不多的思想呀.
严格来讲, 这里的 a.s_name z 字段跟 group by 后的聚合字段, 是没啥关系的, 理应不能出现在此处 1:n 反而造成误解.
更严谨的写法能, 就是将这 a.s_name 也放在 s_id 后面 group by. (熟悉Excel透视表的小伙伴就很清楚啦).
select
a.s_id as "学号",
a.s_name as "姓名",
count(b.c_id) as "选课数",
sum(b.score) as "总成绩"
from student as a
left join score as b
on a.s_id = b.s_id
group by a.s_id, a.s_name;
得到的结果是一样的.嗯, 这个操作, 我以前在用 excel 的时候, 会很频繁使用到, 透视字段这块.
+--------+-----------+-----------+-----------+
| 学号 | 姓名 | 选课数 | 总成绩 |
+--------+-----------+-----------+-----------+
| 0001 | 王二 | 3 | 269 |
| 0002 | 星落 | 2 | 140 |
| 0003 | 胡小适 | 3 | 240 |
| 0004 | 油哥 | 0 | NULL |
+--------+-----------+-----------+-----------+
4 rows in set (0.00 sec)
对于 Null 这里需要处理一下, 比较合理的方式呢, 是填充为 0. 也就是需要在 sum 那的时候, 加上一个判断
case 语法: case when then xxx else xxx end
select
a.s_id as "学号",
a.s_name as "姓名",
count(b.c_id) as "选课数",
sum(case when b.score is Null
then 0
else b.score end
) as "总成绩"
from student as a
left join score as b
on a.s_id = b.s_id
group by a.s_id, a.s_name;
+--------+-----------+-----------+-----------+
| 学号 | 姓名 | 选课数 | 总成绩 |
+--------+-----------+-----------+-----------+
| 0001 | 王二 | 3 | 269 |
| 0002 | 星落 | 2 | 140 |
| 0003 | 胡小适 | 3 | 240 |
| 0004 | 油哥 | 0 | 0 |
+--------+-----------+-----------+-----------+
4 rows in set (0.07 sec)
这样 Null 的问题也解决了. 这种 case when xxx then xxx else xxx end 还是挺常用的.
case 与 if 的区别
if 是条件判断语句 不能在 查询语句中出现; case 是条件检索 可以再查询中出现
小结
- group by + having 的用法 (严谨写法是, 跟 group by 无关的字段, 不要放 select)
- left join 和 inner join 等的不同场景用法 (我感觉工作中用的最多的是 left join 更多一点)
- 深刻理解 apply -> group by -> aggregation , 不论是 sql 还是编程, 数据分析必须迈过的坎
- 字段值判断 case when xx then xxx else xxx end 的这种写法, 跟 if 的区别在于, if 用在函数, case 用在查询.
SQL 强化练习 (二)的更多相关文章
- SQL开发技巧(二)
本系列文章旨在收集在开发过程中遇到的一些常用的SQL语句,然后整理归档,本系列文章基于SQLServer系列,且版本为SQLServer2005及以上-- 文章系列目录 SQL开发技巧(一) SQL开 ...
- SQL总结(二)连表查询
---恢复内容开始--- SQL总结(二)连表查询 连接查询包括合并.内连接.外连接和交叉连接,如果涉及多表查询,了解这些连接的特点很重要. 只有真正了解它们之间的区别,才能正确使用. 1.Union ...
- SQL开发技巧(二) 【转】感觉他写的很好
本文转自: http://www.cnblogs.com/marvin/p/DevelopSQLSkill_2.html 本系列文章旨在收集在开发过程中遇到的一些常用的SQL语句,然后整理归档,本系列 ...
- 数据库基础SQL知识面试题二
数据库基础SQL知识面试题二 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.选课系统SQL语法练习 course数据库中有以下四张表: •students表(学生表): si ...
- C# 动态创建SQL数据库(二) 在.net core web项目中生成二维码 后台Post/Get 请求接口 方式 WebForm 页面ajax 请求后台页面 方法 实现输入框小数多 自动进位展示,编辑时实际值不变 快速掌握Gif动态图实现代码 C#处理和对接HTTP接口请求
C# 动态创建SQL数据库(二) 使用Entity Framework 创建数据库与表 前面文章有说到使用SQL语句动态创建数据库与数据表,这次直接使用Entriy Framwork 的ORM对象关 ...
- SQL注入之二次,加解密,DNS等注入
#sql注入之二次注入 1.注入原理 二次注入可以理解为,构造恶意数据存储在数据库后,恶意数据被读取并进入到了SQL查询语句所导致的注入.恶意数据插入到数据库时被处理的数据又被还原并存储在数据库中,当 ...
- SQL强化(二) 在Oracle 中写代码
一 : 关于查询中的转换 -- 字符串转换 一 : decode 函数 转换 SELECT DECODE ( PROTYPE.PRO_TYPE_DATE, 'L', '长', 'm', '短', ' ...
- SQL编程篇 (二) 定义与流程控制
分类: sql编程:标准的sql 编程 * 纯sql 在标准的编程中又分为 sqlserver-->T-sql oracle-->pl-sql(扩展) 变量:在使用变量之前先定义 声明变量 ...
- Oracle数据库之SQL基础(二)
一.约束 ❤ 1.约束概述 约束作用: (1)定义规则 (2)确保完整性:包括数据的精确性.可靠性.以确保数据不会出错,或者尽量减少出错. 约束的类型: (1)非空约束 (2)主键约束 (3)外键约束 ...
- Spark SQL概念学习系列之为什么使用 Spark SQL?(二)
简单地说,Shark 的下一代技术 是Spark SQL. 由于 Shark 底层依赖于 Hive,这个架构的优势是对传统 Hive 用户可以将 Shark 无缝集成进现有系统运行查询负载. 但是也看 ...
随机推荐
- C# OpenMP
在C#中实现代码优化,并行的方式来提升速度. 参考链接:https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/ho ...
- Postman 接口测试工具详解
一.引言 在软件开发和测试过程中,接口测试是至关重要的环节.Postman 作为一款功能强大的接口测试工具,为开发者和测试人员提供了便捷高效的测试解决方案. 二.Postman 简介 Postman ...
- Windows上安装MySQL详细教程
1.MySQL简介 MySQL是最流行的关系型数据库管理系统,在WEB应用方面MySQL是最好的RDBMS(Relational Database Management System:关系数据库管理系 ...
- Ubuntu通过VMware虚拟机安装步骤
1.下载Ubuntu系统镜像 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17.这个错误需要BIOS CPU里面设置一下,具体问度娘. 18 ...
- 循环(Java篇)
令人头痛的循环(:´д`)ゞ 我们在学习循环的时候可能会有点懵,什么是循环?它可以干嘛?我这里为什么要用循环来写这段代码?等问题. 首先我们来讲一下循环可以干嘛 循环是什么?o(′益`)o 在 Jav ...
- 【前端动画】—— 再看tweenJS
16开始接触前端,一直对一个问题特别感兴趣,那就是js动画,也就是从那时起开始探究动画的各种表现形式,也是那个时候开始意识到编程这块东西最终考验的就是抽象和逻辑,而这一切完全是数学里边的东西. 最早接 ...
- 【ffmpeg】avformat_alloc_context报错System.NotSupportedException不支持所指定的方法
这个错误报了第二次了,网上搜不到靠谱的解决方案,赶快记录一下. 第一个情况:报错如题目System.NotSupportedException 不支持所指定的方法 第二个情况:如果换autogen版本 ...
- Netty源码—2.Reactor线程模型一
大纲 1.关于NioEventLoop的问题整理 2.理解Reactor线程模型主要分三部分 3.NioEventLoop的创建 4.NioEventLoop的启动 1.关于NioEventLoop的 ...
- Git--命令常用
GITLab 命令 git remote add origin https://gitee.com/gtnotgod/Data-Quality-Management.git #增加了远程仓库 git ...
- 我最常用的 Visual Studio 2022 扩展插件推荐:生产力必备工具
Visual Studio 2022作为微软推出的一款功能强大的IDE,业界称之为"宇宙第一IDE".它以出色的性能.丰富的内置功能和对多种编程语言的支持,深受开发者喜爱.然而,随 ...