一个 mysql 表,只有学生姓名`name`和学生成绩`grade`两列,现在要 SELECT 出排名前十的同学,但如果满分的有 12 个,则 SELECT 的结果该有 12 个学生,该如何写 SQL 语句?

2020-09-12 09:40:30 +08:00
 Newyorkcity
谢谢
4808 次点击
所在节点    问与答
58 条回复
zy445566
2020-09-12 10:31:52 +08:00
一条不行就用 2 条!
先查满分人数,如果小于 10 就取前十,大于 10 就取出全部满分
Firewine
2020-09-12 10:36:57 +08:00
@qiayue 这样的话,用代码比 SQL 语句更好的处理
herozzm
2020-09-12 10:38:26 +08:00
@wangsongyan #1 多次查询性能不是最佳
mm163
2020-09-12 10:43:54 +08:00
select * from tts where grade = 100
UNION DISTINCT
(select * from tts order by grade desc limit 10)
djj0809
2020-09-12 10:51:09 +08:00
先用子查询获得排第十的分数,然后取所有大于等于这个分数的人
DelayNoMay
2020-09-12 10:51:45 +08:00
查一次再用代码处理呗
ditel
2020-09-12 10:53:28 +08:00
好像可以用事务来判断
h123123h
2020-09-12 11:07:17 +08:00
窗口函数
rodrick
2020-09-12 11:08:50 +08:00
先 orderby 取分再根据分取人正解 最清晰明了 取前十应该数据量本身不算很大吧
qiayue
2020-09-12 11:13:07 +08:00
不要用一个复杂 SQL 解决复杂需求,有时会多个简单 SQL 不仅耗时更短,还更容易理解
newtype0092
2020-09-12 11:13:30 +08:00
窗口函数就是干这个事的
https://dev.mysql.com/doc/refman/8.0/en/window-function-descriptions.html#function_dense-rank
看最后一个 rank 的例子
herimvane
2020-09-12 11:23:07 +08:00
窗口函数,rank, 不知道 mysql 支持不
wangsongyan
2020-09-12 12:25:27 +08:00
@herozzm 我提供的方案不满足楼主需求,至于效率问题,用子查询啊
romisanic
2020-09-12 14:31:14 +08:00
select * from table where grade in (select t.grade from ( select grade , count(name) from table group by grade order by grade desc limit 10 ) t)

逻辑就是 其实你想要的效果就是按照已经出现的分数排名,想要前十名分数的所有同学,对吧
vone
2020-09-12 15:46:34 +08:00
DENSE_RANK() 可以完美解决。

DENSE_RANK() :
此函数返回结果集分区中每行的排名,排名值没有间断。 特定行的排名等于该特定行之前不同排名值的数量加一。
如果两个或更多行在同一分区中具有相同的排名值,那么每个行将获得相同的排名。 例如,如果两位顶尖销售员具有相同的 SalesYTD 值,则他们的排名值都为一。 接下来 SalesYTD 最高的销售人员排名值为二。 这比所讨论的行之前的不同行的数量多了一。 因此,DENSE_RANK 函数返回的数字没有间断,并且始终具有连续的排名值。

https://docs.microsoft.com/zh-cn/sql/t-sql/functions/dense-rank-transact-sql?view=sql-server-ver15

以下是完整测试代码:
--drop table #SCORE
CREATE TABLE #SCORE
(NAME varchar(32) not null
,GRADE INT not null
)
insert #SCORE(NAME,GRADE) values('学生 1',cast(rand()*100 as int))
insert #SCORE(NAME,GRADE) values('学生 2',cast(rand()*100 as int))
insert #SCORE(NAME,GRADE) values('学生 3',95)
insert #SCORE(NAME,GRADE) values('学生 4',cast(rand()*100 as int))
insert #SCORE(NAME,GRADE) values('学生 5',cast(rand()*100 as int))
insert #SCORE(NAME,GRADE) values('学生 6',95)
insert #SCORE(NAME,GRADE) values('学生 7',cast(rand()*100 as int))
insert #SCORE(NAME,GRADE) values('学生 8',cast(rand()*100 as int))
insert #SCORE(NAME,GRADE) values('学生 9',cast(rand()*100 as int))
insert #SCORE(NAME,GRADE) values('学生 10',80)
insert #SCORE(NAME,GRADE) values('学生 12',cast(rand()*100 as int))
insert #SCORE(NAME,GRADE) values('学生 13',cast(rand()*100 as int))
insert #SCORE(NAME,GRADE) values('学生 14',cast(rand()*100 as int))
insert #SCORE(NAME,GRADE) values('学生 15',80)
insert #SCORE(NAME,GRADE) values('学生 16',cast(rand()*100 as int))
insert #SCORE(NAME,GRADE) values('学生 17',cast(rand()*100 as int))

select * from (
select t.NAME,t.GRADE,DENSE_RANK() over(order by grade desc ) RANK
from #SCORE t
) t
where t.RANK<=10
dustinth
2020-09-12 16:11:52 +08:00
假设表结构为 student (id, name, grade).
完整不考虑效率的 sql

select f.id as id, f.name as name, f.grade as grade from student as f where f.grade >=
(select max(e.grade) as cutgrade from
(select sum(d.acount) as count, d.bgrade as grade from
(select a.cnt as acount, a.grade as agrade, b.grade as bgrade from
(select count(id) as cnt, grade from student group by grade order by grade desc) as a join
(select c.grade as grade from (select distinct grade as grade from student order by grade desc) as c) as b
where a.grade >= b.grade
order by bgrade desc) as d group by grade order by grade desc) as e where e.count >= 10)
reus
2020-09-12 16:31:04 +08:00
@vone 问题他不是想要 rank 少于 10,而是 count(*) 等于或者刚好超过 10 。应该要用递归 CTE 来做
reus
2020-09-12 16:42:04 +08:00
对于每个分数,统计该分数线以上的人数,然后找出第一个人数等于或者超过 10 的分数线,再根据分数线筛选即可

精要是统计“分数线以上”的人数,而不是单一分数的人数

with
-- 所有分数
all_grades as (
select distinct grade from grades
)
-- 每个分数及以上的人数
, nums as (
select grade, (select count(*) from grades g2 where g2.grade >= grade) as num
from all_grades
)
-- 第一个人数等于或者超过 10 的分数线
, i as (
select * from nums
where num >= 10
order by grade desc
limit 1
)
-- 分数线以上的所有人
select * from grades
where grade >= (select grade from i)
967182
2020-09-12 17:03:54 +08:00
select name,grade,rank() over(order by grade) tt from user_grade;
sabercoding
2020-09-12 19:17:29 +08:00
获取 limit10 的成绩,然后再查这些成绩的同学即可。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/706317

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX