说下情况,我们公司同个部门的好几个同事,连个基础的 SQL 都写不好,代码中一堆数据库 N+1 的问题,连个 WHERE IN 查询都不会用,涉及到批量查询,都是遍历然后一条条的去跑 SQL,同一个方法重复查询了好几次,数据库设计更是不行,外键都是用逗号分隔拼接成字符串,然后保存到主表上。
正因为 SQL 写的烂,所以接口性能很差劲,但是他们好像都不在意这些 IO 方面的优化,整天在想着怎么优化语言性能,比如反射、JSON 序列化、语言基础库的性能;要么就是上集群、加缓存,然后又没有任何设计模式,直接业务代码中强硬加入缓存读写,就算是缓存也是先从缓存读出数据,然后遍历一条条去数据库再查出来,性能更差了
其实整体项目量不大,好好写好 SQL,基本上能搞定 90% 的性能问题了,大部分的开发经验也都好几年了,不至于这种基础知识点不懂,可为啥就是不重视 SQL 性能
101
xinJang 2021-09-03 15:39:51 +08:00
@summerLast 试了下,空指针。
我本身 sql 是个模糊搜索的查询,里面本来就有 left join 语句,可能不适合我这种场景 |
102
Canon1014 2021-09-03 15:44:04 +08:00
还不是被逼的
|
103
qrobot 2021-09-03 15:44:06 +08:00 2
因为 SQL 写的再好,比如中间件的一次缓存命中, 如果缓存命中率达到 90% 以上, 那么就不存在 IO 问题。
``` 正因为 SQL 写的烂,所以接口性能很差劲,但是他们好像都不在意这些 IO 方面的优化,整天在想着怎么优化语言性能,比如反射、JSON 序列化、语言基础库的性能;要么就是上集群、加缓存,然后又没有任何设计模式,直接业务代码中强硬加入缓存读写,就算是缓存也是先从缓存读出数据,然后遍历一条条去数据库再查出来,性能更差了 ``` 我不清楚你们的项目结构,不过 IO 在怎么优化是有瓶颈的,为了提高命中缓存, 我觉得不用 in 和 left join 是最好的 |
104
dbpe 2021-09-03 15:45:50 +08:00 2
@xinJang 现在我是用..jooq 或者 querydsl 去解决...不过我还是尽量单表查询,然后去合并查询请求...list 遍历去查不靠谱
|
105
Akiya 2021-09-03 15:47:07 +08:00
这个你得怪市场需求,不要怪同事,公司招什么样的人,就会得到什么样的人
|
106
cedoo22 2021-09-03 15:50:10 +08:00
技术面试到面试数据库知识的时候 分两种, 一种给个场景直接问 SQL 怎么写, 另外一种是问你数据库集群 balabala,
|
107
wh469012917 OP @qrobot 但是我们数据量并不大,并发也不高,基本上写好 sql 能处理大部分场景了,缓存架构什么的等业务起来了再搞也不迟
|
109
followyourheart 2021-09-03 15:58:26 +08:00
@ipwx #66 你说的这种临时表 不应该是我们优化的对象 避免产生临时表吗
|
110
hhjswf 2021-09-03 16:00:23 +08:00
我们总监不让用 join 的...n+1 怎么避免..
|
111
xinJang 2021-09-03 16:07:48 +08:00
@dbpe 没看太明白。如果你是先查单表,拿到的数据是一个 list 吧,这时你不遍历查询怎么组合数据?这里还有个问题,那就是分页数据,组合之后的数据长度有时是不一致的,那就会变成翻第 2 页只有 1 条数据的可能
|
112
flybluewolf 2021-09-03 16:08:10 +08:00
@hhjswf 笑死,关系型数据库这么多年理论研究都白搞?不要 join,那还玩什么集合论?所有关系型数据库都移除 join 操作好了!具体项目具体研究,基本上 99%的项目关系型数据库足够满足要求,性能问题都能通过 sql 语句+索引解决。
|
113
flybluewolf 2021-09-03 16:11:02 +08:00
@dcoder 动则都分布式? CPA 咋解决?
|
114
followyourheart 2021-09-03 16:21:45 +08:00
@hhjswf #110 查出来 list1 然后获取 idList 根据 idList 一次性查出数据集合 Map<id,Object>,然后遍历 list 根据 id 从 Map 取出数据,组装数据,不知道有没有更好的方法
|
115
jun771480011 2021-09-03 16:29:09 +08:00
因为 SQL 写不好,所以张口闭口就是缓存架构分布式!
|
116
dream4ever 2021-09-03 16:29:31 +08:00
@wh469012917 他们这个智商,就告别这一行吧
|
117
l00t 2021-09-03 16:29:32 +08:00 2
|
118
skinny 2021-09-03 16:32:35 +08:00
现在这行不就流行堆砌分布式、集群、高并发、高性能、高可用等等这些看似高大上名词的风气吗?
连那些云服务大厂都一个鸟样,吹起来什么异地灾备、自动化、高可用有多少个 9,崩起来就 N 小时还全面崩 |
119
followyourheart 2021-09-03 16:40:28 +08:00
@l00t #117 查出最低分大于 80 的 group by 不就行了吗
|
120
dream4ever 2021-09-03 16:42:10 +08:00
@sagaxu 我一同事负责的一项业务有不少 SQL 查询,前一段时间发现服务器 CPU 经常飙到 100%,排查来排查去发现一堆慢查询,最后追根溯源发现数据库各个表所有被查询到的字段都没有设置索引……
|
121
zhouyou457 2021-09-03 16:48:23 +08:00
简直就是在说我司的开发!
手上的项目有 n 多一句 sql 完成的功能,什么先查出 id 再 in 查询,各种拼接子查询,结果合并计算、标准时间格式化成前端格式字符串,然后结果集一律字符串返回,连封装都懒得写,直接 Map<String,String> |
122
wh469012917 OP @l00t 为什么要设计一个成绩表?是因为一个学生一个科目,可能存在多次考试吗?如果是的话,所有科目成绩 >= 80 是指这个科目的每次成绩,还是某一次成绩?
|
123
zgcwkj 2021-09-03 17:01:27 +08:00
@zhouyou457 先查询出 ID,可能是嵌套的 sql 会影响性能吧,我试过直接用 in(selete id) 的时候会特别慢的。我认为不能直接否定,得看需求。嵌套的查询很影响 sql 性能的,从而影响其他的业务
|
124
l00t 2021-09-03 17:02:34 +08:00
#122 就一次考试
|
125
l00t 2021-09-03 17:03:17 +08:00
另外你的 ID 是不是犯禁啊。一 @就会触发验证。是不是 69 两个数字导致的?
|
126
l00t 2021-09-03 17:03:40 +08:00
咦好像也不是,这次就没触发
|
127
ytmsdy 2021-09-03 17:04:01 +08:00
@xz410236056 DBA 一般是解决数据库底层的一些性能问题,比如说 cache 命中率过低,根据情况设置索引,设置一些联合主键等问题。但是到开发层面的基础 SQL 编写,一般是不会介入的,要不然 DBA 要累死!
|
128
l00t 2021-09-03 17:04:54 +08:00
@followyourheart 题很简单,能写对的十不存一,我也很惊讶。
|
130
ytmsdy 2021-09-03 17:13:49 +08:00
现在的开发越来越脱离底层开发,上手就是 ORM 一般梭哈,连 SQL 都不需要会,也能做 CRUD 。
从另外一个方面来看,越来越多非科班出身,直接在培训班速成出来的开发人员,虽然看着很唬人,但是涉及到一些具体算法,数据库设计,性能分析就完全抓瞎了。 公司的管理层也只管功能上线能用就好了,技术层面的代码实现,业务逻辑处理完全不会! |
131
Brentwans 2021-09-03 17:14:19 +08:00
的确,特别是去有钱但是 IT 部门没什么事的客户,很多小问题数据库完全可见处理的,一定要上 XXX 项目。我曾经处理过一个客户,处理 XXX 一定要上 spark,但是机器就 3 台老服务器,内存 16G...无所谓的,你可以想一下为什么这个破机器要上 Spark ?达到目的皆大欢喜
解决问题有的方法简单,有复杂的,有些看似简单实际运维复杂,有的还有利益关系。甚至有时候刚好研发同学最近看了某些工具或者方案,于是就用了。 我现在觉得最终能够稳定解决问题的方案,至少不算差,至于其他的不太重要了。 |
132
Cbdy 2021-09-03 17:18:01 +08:00 via Android
但是,代码写得好,有用吗?
事实是没用,所以写得烂 自己的项目当然可以好好写,但将心比心,大多数人都是外出务工的农民工,能跑通过验收老板能接受就行了,何况还有 996 、PUA 、拖欠工资、251 能大山压着呢 至于分布式集群、消息队列这种听起来高大上的东西当然要背得头头是道,这样才能提高身价,让老板满意,让面试官满意 |
133
Veneris 2021-09-03 17:37:01 +08:00
唱个反调,有一部分愿意是这样的。
复杂 sql 可维护性差,绝大部分公司的绝大部分业务,都不会对 sql 如此苛刻,命中索引足够了。 但是人员的流动却是常事,业务逻辑都在代码里会更好上手,随便找个实习生也能接手代码。 项目的平稳发展比所谓的写好 sql 更重要。 |
134
lap510200 2021-09-03 17:45:20 +08:00
小公司小项目就这样,很多人写代码, 表面上运行没问题,但是 ide 报各种警告异常,像我这种洁癖的人,看着很难受,看都懒得看,很多还不是科班的,有的人还比较倔的,不好说的
|
135
Macolor21 2021-09-03 17:53:47 +08:00
@hhjswf 看一下 GraphQL 怎么解决 N+1 的,用 DataLoader,核心思想是 @followyourheart 提到的,list1 拿到里面的关联键,通过这个键( id )集合用 in 一条查询出来,然后映射成 Map<id,List Or Object>
|
136
cassyfar 2021-09-03 17:54:54 +08:00
其实我觉得挺正常的,大厂挺多人都没机会接触 sql 了。选择后端数据库都是 nosql 的。
|
137
wh469012917 OP @Cbdy 代码写得好是没用,但是写得烂就有用吗?“996 、PUA 、拖欠工资、251” 这些问题不会因为你代码写的烂就能解决。而且相对好的代码,后续自己维护和版本迭代,对开发效率之类都是有好处的
|
138
wh469012917 OP @Veneris 你去试试一个,n+1 查询,命中索引,看看整体的时间怎么样就知道了
|
139
Cbdy 2021-09-03 18:21:31 +08:00 via Android 1
@wh469012917 写得好的代码要花时间,写得烂的代码可以更快出活,这样就可以 996,避免 700,剩下一天休息一下舒缓一下 PUA 的压力,至于后续维护和版本迭代,就管不了这么多了,谁知道后续还是不是原作者维护和迭代呢
我不是说这是对的实践,但是业界事实,毕竟成年人的世界没有对错,只有打工 |
140
rsyjjsn 2021-09-03 18:29:18 +08:00
理想情况是每个人都好好写 sql,其实 bug 会更少,但是有用吗?现在的招聘动不动就是分布式集群以及 k8s,工作了 1 年,这些总得去学去实践吧,那怎么实践呢?那当然是在现成的项目里面实践啊,难不成我写了 1 年代码,整天研究 sql 怎么写更加快?面试官可不会问你那么多 sql 相关问题。
|
141
zhuichen 2021-09-03 18:30:19 +08:00
主要是看你们部门 leader 怎么看这个问题,如果 care 的话,你就可以提出来,就有机会发展,不过这么久了,我估计大概率不 care,久而久之就这样了
|
142
adoal 2021-09-03 21:48:47 +08:00 via iPhone
这就是所谓的“互联网公司”
|
144
lanlanye 2021-09-04 00:04:31 +08:00
看完两页希望有人来解个惑:为什么说数据量大的场景下联表会有坑?有的话常用的解决方式是什么?
|
145
wellsc 2021-09-04 00:11:51 +08:00
报我身份证得了
|
147
wellsc 2021-09-04 00:13:12 +08:00
啊不对 我对“天在想着怎么优化语言性能,比如反射、JSON 序列化、语言基础库的性能;要么就是上集群、加缓存,然后又没有任何设计模式” 也是很方案,后端还是操作系统更重要的,而不是编译原理
|
148
dcoder 2021-09-04 08:07:12 +08:00
@wellsc 就是被楼主 @wh469012917 吐槽的同事么?
|
149
vishun 2021-09-04 08:33:10 +08:00
@l00t
先在考试成绩表中查找分数大于 80 的按用户分组的个数,然后再和科目总个数比较? ``` select user_id from ( select count(*) as num,user_id from 成绩表 where score >80 group by user_id ) as t where t.num = (select count(*) from 科目表) ``` |
150
xuanbg 2021-09-04 10:06:35 +08:00
|
151
cnbattle 2021-09-04 10:33:10 +08:00
想知道 lz 理解的基础的 SQL 包含哪些? 我目前基本不用子查询 jion 以及视图 存储过程啥的
|
152
crazycarry 2021-09-04 10:51:34 +08:00
你接触的太少,c 端基本就是查询 es 或者 redis 的数据了
|
154
fengpan567 2021-09-04 11:09:37 +08:00
@xuanbg 是不是得把 name 加到 group by 里额
|
158
wh469012917 OP @zhuichen 部门领导自己没写代码,之前我有委婉提出过这个问题,领导意思就是说要改,但是他也没去推动这件事,我大概就知道他意思了
|
159
wh469012917 OP @cnbattle 我理解的基础,就是要懂得怎么避免 n+1 的问题,以及在合适的场景下用 join 查询,以及 where in 查询。这里“合适的场景”的定义是:通过主键的集合查询列表,可以使用 where in 来,而不是遍历集合一条条 where id =?这样
|
160
wh469012917 OP @crazycarry 如果数据量不大百万级别,并发量也不大,加 es 或者 redis 的意义在哪里?
|
161
veike 2021-09-04 12:25:58 +08:00 via Android
@wh469012917 领导跟你感受估计一样,想改也改不了
|
162
857681664 2021-09-04 13:00:06 +08:00 via Android
@l00t 这题比较标准的解法是 join 表后 group by 学生 id 再 having 分数> 80 分的数=科目数吧,而且还要考虑到有学生没考过试的
|
163
ericbize 2021-09-04 13:17:39 +08:00
那是因为你没见过 一个 select * 可以完事的,别人 select 第一列 ,select 第二列 ,select 第三列 (每次只出一个数据那种)
|
164
alexkkaa 2021-09-04 13:18:32 +08:00
这叫快餐式开发
|
165
mynamewang0 2021-09-04 13:23:14 +08:00
@l00t 这样写应该可以吧,没运行。应该不需要用到聚合函数
select * from student std where not exist ( select 1 from subject sbj inner join scorce sc on sc.subjectId = sbj.subjectId where std.studentId = sbj.studentId and sbj.scorce < 80 ) |
166
2i2Re2PLMaDnghL 2021-09-04 13:25:24 +08:00
话说起来,不记得是谁,推荐任何人想要学第二门语言都应该学 SQL
争什么 Python Go JavaScript 什么的,第二门语言最应该是 SQL 不少人推荐第二门语言采用不同的范式;而 SQL 可以说是目前生产环境上广泛运用的唯一一门描述式语言了。 顺便摘个发言:『如果你的同事全都不如你,那么是你该跳槽的时候了』 |
168
sunznx 2021-09-04 13:30:16 +08:00
IT 行业盛产臭鱼烂虾
|
169
namelosw 2021-09-04 13:52:11 +08:00
N + 1 是基础 + 原则问题,这个都不当回事要我我就铁拳了。这个达不到,谈别的都是吹逼。
就算不喜欢写 SQL,也得搞 batch 解决这个问题才行。 至于 SQL 为啥经常被忽视是因为 SQL 逼格比分布式低,用得早,想到 DBA 和存储过程就能想到那些老掉牙的项目。所以人们都喜欢拿分布式包装自己。 |
170
onhao 2021-09-04 14:38:27 +08:00
|
171
potatowish 2021-09-04 14:58:10 +08:00 via iPhone
面试主要就问这些啊,现在面三年以上的几乎没有笔试,谈起 SQL 优化头头是道,真要是手写没几个能过的
|
172
iseki 2021-09-04 15:14:17 +08:00
总觉得是一些公司推广自己的“规范”,加上一些人无视实际情况盲目使用其他公司的规范导致的麻烦。我相信制定这些规范的程序员知道自己在干什么,但是我非常质疑那些盲目执行这些规范的人知不知道自己在干什么。
|
173
anouser 2021-09-04 15:14:40 +08:00
好奇你们这个产品有多少人在使用?
|
174
msg7086 2021-09-04 16:33:52 +08:00
N+1,项目初期,或者量小的时候,可以不用优化。
但是数据量大了,开始针对热点优化的时候,还不知道去改,那是真的不应该。 当然,有些坑不亲自跳一次是不知道的,这些人可能就是经验太少,没跳过这坑,你只要不提他们就根本不知道要这么做。 |
175
xuanbg 2021-09-04 16:48:52 +08:00
@onhao 你这个写错了,学生只要有一门考过 80 就符合条件了。要先按学生分组,取最小值,最小值也高于 80 就没毛病了。所以要用 having 去筛而不是 where 。
|
176
leafre 2021-09-04 16:56:35 +08:00
面向面试编程
|
177
unregister 2021-09-04 17:58:25 +08:00
sql 不一定要用 join,我目前都是单表查的,再用 dto 组装也可以。
|
178
JerryCha 2021-09-04 21:52:32 +08:00
因为这是时尚
你不跟着走,哪怕性能好也是落伍 |
179
shot 2021-09-05 12:13:02 +08:00 1
> 我们公司同个部门的好几个同事
多个成员存在这样的问题,说明这是团队(负责人)的问题,而不是孤立的某几个人的问题。 如果要从团队层面系统地解决这个问题,推荐两个实践: 1 )引入压力测试 /性能优化,对于数据量千万级以下的传统应用,要求单机支持 1k+ tps 、100- ms latency,可以在设计 /开发 /测试环境快速识别性能瓶颈,避免低质量的设计和开发; 2 )引入 Application Performance Monitoring 工具,数字化直观展现应用在生产环境的性能瓶颈,将慢操作视为高优先级技术债务,对应的产品有 SkyWalking 、New Relic 等等。 通过建立体系化的开发流程,即使新的工程师加入时没有相关的经验和意识,处理几次相关问题后也能逐渐适应和融入。 当然,这对团队负责人的技术能力和管理能力要求就比较高了。 如果楼主部门领导不具备这种能力,楼主描述的问题必然会反复出现。 |
180
noparking188 2021-09-05 13:11:38 +08:00
有库表设计和 SQL 规范嘛
|
181
glfpes 2021-09-05 16:35:09 +08:00
SQL 写好并不容易,做一个 sql boy 比 crud 要困难。
服务治理其实就是 crud |
182
ychost 2021-09-05 19:24:24 +08:00
高并发+大流量 本质是 DB 、缓存 的使用,语言层面能够优化的性能微乎其微
|
183
cyru1s 2021-09-05 19:38:37 +08:00
还不是各个大厂面试官逼得。
实际工作:在 Druid 之类的存储上写类 SQL 的 DSL 、往往还因为公司版本过低不支持最新文档的 functions,最终你要合理的把查询拆成几个然后本地处理中间数据、最终完成一个功能。 面试提问:缓存架构分布式。 |
184
wh469012917 OP @anouser 用户量大概十几万吧,日活 1 万左右,做垂直领域的,量不算多,但是还是有用户用的
|
185
wh469012917 OP @namelosw 对的,解决一个 n+1 问题,比你用什么架构缓存都好使,除非就是为了装逼
|
186
wh469012917 OP @msg7086 因为 n+1 问题,我们目前部分接口已经出现明显的性能问题了,但是有些人都不重视这块,一直在优化语言级别的性能
|
187
wh469012917 OP @unregister 也可以,join 不是万能,解决问题的方案有很多种,
|
188
wh469012917 OP @shot 团队负责人虽然也是技术出身,但是自己并不写代码,主要是注重开发进度和应用稳定性,技术主要让组内一个有一定年限的同事来处理,但是这代码就是这个同事写出来了,所以就不要指望他能做啥优化了。
目前数据量在三千万左右,每个月增长差不多 200w 左右,sql 性能问题已经慢慢显露出来了。 |
189
wh469012917 OP @noparking188 无,一个数据库出现 4 种字符集,join 都用不了;字段命名没有规则,大家各做各的
|
190
wh469012917 OP @ychost 对的,数据库是 IO 最重的地方,应该花大力气来处理;加缓存虽然效果明显,但是会导致代码复杂度增加和缓存同步问题
|
192
msg7086 2021-09-06 04:40:41 +08:00
楼上说的对,出现这些问题,但是同事都不懂,那应该搞一些研讨会学习会,让一两个懂的人出来给其他人讲解一下。然后 leader 也应该制定规范,让大家知道哪些东西是必须要遵守的。
从你说的来看,这两个 leader 都有些问题,工作没有做好,导致下面拉稀。 |
193
wh469012917 OP @msg7086 前提是要有人懂,并且有一定的话语权来推动呀;我们这边部门领导一般不管这些,主要负责全局的把控;后端组长自己就是写这些代码的人;我是一直想要推动,奈何没什么话语权,所以挺难的
|
194
wh469012917 OP @noparking188 一个数据库出现了四五种字符集,你觉得能有什么规范吗
|
195
noparking188 2021-09-06 09:31:35 +08:00
@wh469012917 #194 不大懂你说的四五种字符集什么意思,是 collate character ?
如果是,在规范里指定用哪种就行,原先不统一的字符集后面陆续更改,解决历史遗留问题 |
197
BiChengfei 2021-09-06 14:14:03 +08:00
@l00t 这不是本科《数据库原理》中的 demo 吗?我会写,我是不是面试通过了,嘿嘿嘿
create table t_student ( id integer primary key , name varchar(40) ); create table t_course ( id integer primary key , course varchar(40) ); create table t_grade ( id integer primary key , student_id integer, course_id integer, grade integer ); insert into t_student(id, name) values (1, '张三'); insert into t_student(id, name) values (2, '王五'); insert into t_student(id, name) values (3, '小李'); insert into t_student(id, name) values (4, '王华'); insert into t_course(id, course) values ( 1, '数学' ); insert into t_course(id, course) values ( 2, '语文' ); insert into t_course(id, course) values ( 3, '英语' ); insert into t_course(id, course) values ( 4, '化学' ); insert into t_course(id, course) values ( 5, '物理' ); insert into t_grade(id, student_id, course_id, grade) values ( 1, 1, 1, 100 ); insert into t_grade(id, student_id, course_id, grade) values ( 2, 1, 2, 80 ); insert into t_grade(id, student_id, course_id, grade) values ( 3, 1, 3, 10 ); insert into t_grade(id, student_id, course_id, grade) values ( 4, 1, 4, 100 ); insert into t_grade(id, student_id, course_id, grade) values ( 5, 1, 5, 100 ); insert into t_grade(id, student_id, course_id, grade) values ( 6, 2, 1, 100 ); insert into t_grade(id, student_id, course_id, grade) values ( 7, 2, 1, 100 ); insert into t_grade(id, student_id, course_id, grade) values ( 8, 2, 1, 100 ); insert into t_grade(id, student_id, course_id, grade) values ( 9, 2, 1, 100 ); insert into t_grade(id, student_id, course_id, grade) values ( 10, 2, 1, 100 ); select a.student_id, b.name from t_grade a left join t_student b on a.student_id = b.id left join t_course c on a.course_id = c.id group by a.student_id having min(a.grade) >= 80 |
198
stevenbipt 2021-09-06 14:15:22 +08:00
基础的 sql 都写不好,简单的调优分析都做不好就谈那些理论上的这种操作个人是接受不了,就算接口慢优化也要分析好哪些地方该优化,而不是直接上理论安排上一堆东西,存储过程这些复杂的做不好问题不大,但是基础的 sql 查询都写得烂那还是建议换一家不用关系型数据库的公司吧
|
199
l00t 2021-09-06 14:49:10 +08:00
@mynamewang0 你这个 inner join 没意义啊……
@onhao “所有” 修饰 科目…… @BiChengfei left join course 这步不对 @xuanbg 加科目表是因为有人可能缺考。当然这个也基于一个没考就没记录的假设。如果没考也有一条得分为 0 的记录,那科目表有没有也没关系。我一般会在追问时才要求考虑没考就没记录的情况。你这条语句的思路是对的,但是写得太随意了,过不了语法编译的…… @vishun 我一般不会这么写,但是你这样写也行……反正结果是对的…… |
200
BiChengfei 2021-09-06 17:20:34 +08:00
@l00t 之前的不对是因为没有考虑缺考吗,大意了
-- t_student 、t_course 全连接,然后左连接成绩表,缺考就为 null select a 。id, a 。name from t_student a left join t_course b on true left join t_grade c on a 。id = c 。student_id and b 。id = c 。course_id group by a 。id having min(c 。grade) >= 80; -- t_grade 左连接 t_student,通过学生 id 分组,然后最低分和参考课程数过滤 select a 。id, b 。name from t_grade a left join t_student b on a 。student_id = b 。id group by a 。student_id having min(a 。grade) >= 80 and count(a 。course_id) = (select count(*) from t_course); 第一种是第一想法,但会占用额外的内存,第二种会好很多 请不要在每一个回复中都包括外链,这看起来像是在 spamming 我只能把点换成句号 |