新闻详情

新闻详情

首页 / 资讯中心 / 详情

SQL Server开窗函数partition by实战:成绩排名不再求GROUP BY

发布时间:2026/10/2 14:39:03来源:尧图网络
SQL Server开窗函数partition by实战:成绩排名不再求GROUP BY
排成绩这件事只要是做数据库开发的迟早都会碰上。我印象特别深的一次是刚接触MS SQL Server那年领导甩给我一张几百人的考试成绩表要求按班级排名、按总分排名、还要把每科排名都列出来。我一听就懵了——用GROUP BY做分组统计结果行数全被压缩了用子查询关联排名SQL写得跟迷宫似的一改条件就崩。后来翻了半天资料才搞明白真正顺手的是partition by这个开窗函数。它一个函数就能把分组和排名同时搞定行数还不变。这篇文章就围绕成绩排名这个实战场景把partition by的用法、原理和那些踩过的坑一次说清楚。1. 成绩排名需求背后的核心痛点为什么GROUP BY不够用先聊一个最基础的问题排名这个需求用GROUP BY真的做不了吗能做但做得很别扭。GROUP BY的本质是分组压缩你按班级分组出来的就是每个班级一行汇总你按科目分组出来的就是每科一行汇总。可成绩排名要的是什么呢是每个人一行原始记录但旁边多出来一个名次列。这两者的逻辑天然冲突。举个例子一张考试成绩表长这样学号姓名班级科目成绩1001张三1班数学921001张三1班语文881001张三1班英语761002李四1班数学851002李四1班语文911002李四1班英语80如果你想按班级看数学成绩的排名GROUP BY班级当然可以但结果只有两行张三和李四每个人的其他科目信息全丢了。你还得另外再关联回原表把明细捞出来一来二去SQL就膨胀得没法看。而且GROUP BY还有个老毛病它没法在聚合的同时保留明细。你既要某个人的原始成绩又要这个成绩在全班的位次用GROUP BY就得做两次查询再拼起来性能差不说代码维护起来也痛苦。那这时候开窗函数就显出价值了。开窗函数最大的特点是它在不改变行数的情况下给每一行附加一个窗口内计算出来的值。partition by就是窗口的分组条件你可以按班级开一个窗口、按科目再开一个窗口同一行数据可以同时参与多个窗口的计算互不干扰。这就完美解决了既要明细、又要排名的双重需求。我在实际项目里还发现一个更麻烦的场景有些报表需要总分排名和单科排名同时展示。用GROUP BY你得写两个查询再union或者join而用partition by只在一条SELECT里分别给不同窗口开窗就行代码量直接少一半。这也是我后来在所有排名类需求里首选partition by的根本原因。2. partition by的执行逻辑拆解先分组、再排序、后计算很多初学者会把partition by理解成高级的GROUP BY这个理解不算错但不精准。partition by确实有分组的功能但它分完组之后干的事情完全不同。搞清楚它内部的执行顺序写出来的SQL才能得心应手。2.1 窗口函数的逻辑执行顺序一条带窗口函数的SQL逻辑上大致按这样的顺序走FROM子句先把表加载进来WHERE子句把不需要的行筛掉GROUP BY / HAVING做分组和分组后的过滤如果有的话窗口函数在分组结果之上逐行计算SELECT把最终列投影出来ORDER BY做最终的排序输出也就是说partition by的分组发生在WHERE过滤之后而不是之前。这个顺序非常重要。你如果想排除某些人的成绩再排名直接在WHERE里加条件就行开窗函数会自动基于过滤后的结果集去分组不需要额外的子查询。很多人不知道这点非要多套一层子查询白白增加复杂度。另外要注意窗口函数的执行不改变查询结果的行数。每一行原始记录都会保留只是在旁边多了一个计算列。这就完全是给明细表加排名列的天然工具。2.2 一个简单的partition by排名示例咱们直接用MS SQL Server的语法写一段标准成绩排名SQL。假设有张成绩表ScoreInfo字段包括StudentID、StudentName、ClassName、SubjectName、Score现在要给每个班级的数学成绩做排名SELECT StudentID, StudentName, ClassName, SubjectName, Score, ROW_NUMBER() OVER (PARTITION BY ClassName ORDER BY Score DESC) AS RankInClass FROM ScoreInfo WHERE SubjectName 数学 ORDER BY ClassName, RankInClass;这段SQL做了三件事先用WHERE把科目限定成数学再用PARTITION BY ClassName把数据按班级切成一个个独立的小组最后在每个小组内部按Score降序编号得到班级内排名。输出的结果里每一行还是原来的明细数据只是多了个RankInClass列。这段SQL里最容易写错的地方是ORDER BY那个子句的方向。排成绩肯定要高分在前所以Score后面必须跟DESC你要是写成ASC那就是倒着排了成绩最差的反而是第1名这种低级错误真发生过。另外窗口函数里的ORDER BY和最终结果集的ORDER BY是两个概念前者决定排名计算的顺序后者决定展示顺序两个都可以写也可以只写一个但别搞混。2.3 partition by和group by的本质对比为了让你彻底搞清楚这两个家伙的区别我拿一张表直接对比对比项GROUP BYPARTITION BY行数变化每个分组合并成一行行数保持不变明细保留不保留只能看聚合结果每一行都保留聚合函数SUM、AVG、COUNT等都能用也能用但必须配合OVER与排名配合不支持排名支持ROW_NUMBER/RANK/DENSE_RANK典型场景报表汇总排名、同比、占比、移动平均一句话总结GROUP BY是把多行压成一行PARTITION BY是把一行放到多个组里各自算。业务上如果只是要汇总数用GROUP BY如果要数带着明细一起展示就必须用PARTITION BY。还有一个细节容易忽略PARTITION BY后面可以跟多个列比如PARTITION BY ClassName, SubjectName那就等于按班级科目的联合维度分组每个组合内部独立排名。这种多字段分组的写法在生成复杂报表时特别常用后面实战部分会专门演示。3. 成绩排名的三大核心函数ROW_NUMBER、RANK、DENSE_RANK的取舍讲完了partition by的分组逻辑接下来就是重头戏——排名函数本身。MS SQL Server里常用的排名函数有三个ROW_NUMBER、RANK、DENSE_RANK。它们都配partition by用但排名规则不一样选错了报表上的名次可就是另一回事了。3.1 三个函数的区别与适用场景我先用一个极端的例子展示差异。假设某班数学成绩有四个学生A考了95B考了95C考了90D考了85。学生成绩ROW_NUMBERRANKDENSE_RANKA95111B95211C90332D85443看到区别没有ROW_NUMBER不看成绩是否相同强制按顺序给每个学生一个唯一序号。A、B成绩一样但一个1、一个2。适合取前N条的场景比如每个班取前三个人。RANK成绩相同的人名次并列但下一个名次要跳号。A、B并列第1C直接排到第3。这正是Excel里RANK函数有并列则占用名次的行为符合大多数比赛规则。DENSE_RANK成绩相同名次并列但下一个名次不跳号。A、B并列第1C排第2D排第3。适合名次必须连续的排行榜比如积分榜。我自己的经验是做成绩排名默认先问业务需求。领导说分数一样就并列但名次不要断用DENSE_RANK领导说并列没关系但后面的人名次往后顺延用RANK领导说每个班只取前三名名额按人数算不搞并列就用ROW_NUMBER再去嵌套查一下RN 3就行。3.2 三大排名函数的完整SQL写法直接上代码。还是那张ScoreInfo表分别写三个排名SELECT StudentName, ClassName, Score, ROW_NUMBER() OVER (PARTITION BY ClassName ORDER BY Score DESC) AS RowNumRank, RANK() OVER (PARTITION BY ClassName ORDER BY Score DESC) AS RankRank, DENSE_RANK() OVER (PARTITION BY ClassName ORDER BY Score DESC) AS DenseRankRank FROM ScoreInfo WHERE SubjectName 数学 ORDER BY ClassName, Score DESC;运行结果里RowNumRank、RankRank、DenseRankRank三列并排出现一眼就能看出三个函数的名次差异。我建议你在本地跑一遍这个SQL观察一下数据比看十篇文章都管用。3.3 为什么说RANK是最容易被误解的函数RANK这个函数名看起来最正统很多人直接拿它当排名用但结果经常跟预期不一样。原因在于RANK遇到并列会跳号很多业务方并不接受第1名后面直接是第3名。我有一次做学生成绩分析用了RANK结果领导看到报表直接问第2名哪去了场面非常尴尬。从那以后我每次都要问清楚需求并列的时候名次是顺延还是打断。这里也分享一个判断技巧如果业务系统里名次后面还跟着奖金、名额等资源分配一般要用DENSE_RANK因为名次断了不连续资源档次没法对应如果只是做个成绩从高到低的展示序号用ROW_NUMBER反而最稳妥——至少名单上每一个学生都有一个唯一的序号不会让人数都对不上。4. partition by在成绩分析里的其他打开方式聚合、极值、前后对比排名只是partition by的入门用法。实际做成绩分析的时候我还会用partition by干很多别的事全都围绕那一条又不能少的明细数据。4.1 用partition by配合聚合函数做占比和加总开窗函数里可以用SUM、AVG、COUNT等聚合函数但语法上要加OVER子句。比如要给每个学生算数学成绩占班级数学总分的比例直接这样写SELECT StudentName, ClassName, Score, SUM(Score) OVER (PARTITION BY ClassName) AS ClassTotalScore, CAST(Score * 100.0 / SUM(Score) OVER (PARTITION BY ClassName) AS DECIMAL(5,2)) AS ScorePercent FROM ScoreInfo WHERE SubjectName 数学 ORDER BY ClassName, Score DESC;这段SQL里SUM(Score) OVER (PARTITION BY ClassName)的含义是在每一行数据上计算当前班级所有数学成绩的总分。它不压缩行数所以每一行都能看到自己班级的总分再除以自己的成绩就是个人占比。有没有感觉很方便如果不用开窗函数你要先查一遍班级总分再关联回去写起来至少多三行代码。同理AVG(Score) OVER (PARTITION BY ClassName)能直接在每一行上算出班级平均分用来跟个人成绩比较判断高于班级平均还是低于班级平均。这类明细行上带汇总值的需求在成绩分析报表里非常常见。4.2 用FIRST_VALUE和LAST_VALUE提取组内极值还有一个比较冷门但实战好用的函数FIRST_VALUE。它可以在每个窗口内取第一个值。比如想看一下每个班级数学最高分是谁SELECT StudentName, ClassName, Score, FIRST_VALUE(StudentName) OVER (PARTITION BY ClassName ORDER BY Score DESC) AS TopStudent FROM ScoreInfo WHERE SubjectName 数学 ORDER BY ClassName, Score DESC;这里需要特别注意FIRST_VALUE默认的窗口范围是整个分组但如果你在OVER里写了ROWS BETWEEN这样的帧条件取值的范围就会变化。默认情况下FIRST_VALUE配合ORDER BY SCORE DESC就是取分组内按成绩排序后的第一个人的姓名也就是最高分的那个人。这个函数在做跟第一名比差几分这类分析时尤其好用Score - FIRST_VALUE(Score) OVER (PARTITION BY ClassName ORDER BY Score DESC) AS DiffFromTop直接算出每个学生跟班级最高分的差距。做学情分析时这个差距比单纯的名次更有说服力。4.3 用LAG和LEAD对比前后名次的成绩变化LAG和LEAD这两个函数可以取当前行的前一行或后一行的某个字段值。在成绩排名场景里配合partition by使用可以看前一名的成绩是多少、我差多少才能超越他。SELECT StudentName, ClassName, Score, ROW_NUMBER() OVER (PARTITION BY ClassName ORDER BY Score DESC) AS RankNum, LAG(Score) OVER (PARTITION BY ClassName ORDER BY Score DESC) AS PrevScore, Score - LAG(Score) OVER (PARTITION BY ClassName ORDER BY Score DESC) AS GapToPrev FROM ScoreInfo WHERE SubjectName 数学 ORDER BY ClassName, Score DESC;结果中PrevScore列会显示每个学生前一名的成绩GapToPrev显示跟前一名的分差。第1名的PrevScore是NULL因为前面没人。这个写法在进步名次分析里也很有用把两次考试成绩放在同一张表里用LAG对比上次成绩就能算出分数涨跌。有的朋友会问为什么不用自连接来实现确实能实现但自连接要关联两次还得处理NULL和边界行SQL复杂不说性能也差。LAG/LEAD是原生优化过的执行效率高代码也直观。4.4 用AVG配合帧计算做移动平均还有一种很常见的需求既要看学生逐次考试成绩的波动又要看一个平滑的移动平均线。这种需求在成绩趋势分析里特别多。MSSQL的开窗函数里可以定义帧(BETWEEN...AND...)来控制计算范围SELECT StudentName, ExamDate, Score, AVG(Score) OVER (PARTITION BY StudentName ORDER BY ExamDate ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS MovingAvg3 FROM ScoreInfo WHERE StudentName 张三 ORDER BY ExamDate;这段SQL会计算张三每次考试前三次成绩含当前这次的移动平均分。移动平均能消除单次考试的偶然波动更稳定地反映成绩趋势。这里帧的定义是当前行往前数2行到当前行也就是窗口大小为3。这类计算如果不用开窗函数你得在应用程序里循环处理或者写复杂的子查询用了开窗函数一条SQL就搞定而且数据量越大优势越明显。5. 综合实战分班成绩排名报表的完整落地理论说了这么多接下来走一遍完整实战。我会从建表、造数开始一步步写出能直接抄作业的SQL覆盖单科排名、总分排名、各科前N名、名次变化等几个高频需求。5.1 准备测试数据先建一张学生成绩表插入一些测试数据CREATE TABLE ScoreInfo ( StudentID INT, StudentName NVARCHAR(50), ClassName NVARCHAR(20), SubjectName NVARCHAR(20), Score DECIMAL(5,1), ExamDate DATE ); INSERT INTO ScoreInfo VALUES (1001, N张三, N1班, N数学, 92.0, 2024-06-01), (1001, N张三, N1班, N语文, 88.0, 2024-06-01), (1001, N张三, N1班, N英语, 76.0, 2024-06-01), (1002, N李四, N1班, N数学, 85.0, 2024-06-01), (1002, N李四, N1班, N语文, 91.0, 2024-06-01), (1002, N李四, N1班, N英语, 80.0, 2024-06-01), (1003, N王五, N1班, N数学, 92.0, 2024-06-01), (1003, N王五, N1班, N语文, 79.0, 2024-06-01), (1003, N王五, N1班, N英语, 85.0, 2024-06-01), (1004, N赵六, N2班, N数学, 78.0, 2024-06-01), (1004, N赵六, N2班, N语文, 84.0, 2024-06-01), (1004, N赵六, N2班, N英语, 90.0, 2024-06-01), (1005, N钱七, N2班, N数学, 88.0, 2024-06-01), (1005, N钱七, N2班, N语文, 72.0, 2024-06-01), (1005, N钱七, N2班, N英语, 83.0, 2024-06-01);注意这里我特意让张三和王五的数学都是92分为的就是演示并列情况下的排名差异。5.2 场景一每个班级单科成绩排名需求按班级给数学成绩排名并列名次要顺延用RANK。SELECT StudentName, ClassName, Score, RANK() OVER (PARTITION BY ClassName ORDER BY Score DESC) AS RankByClass FROM ScoreInfo WHERE SubjectName 数学 ORDER BY ClassName, RankByClass;执行结果StudentNameClassNameScoreRankByClass张三1班92.01王五1班92.01李四1班85.03钱七2班88.01赵六2班78.02张三和王五并列第1李四直接排第3这就是RANK的跳号行为。如果你想要不跳号的版本把RANK换成DENSE_RANK李四就会显示第2名。5.3 场景二每个学生的总分班级排名排名之前先要把各科成绩汇总成总分然后再按班级排名。这里有个关键坑窗口函数不能直接作用在GROUP BY的结果上你得先分组算总分再在外面套一层窗口排名。WITH TotalScore AS ( SELECT StudentID, StudentName, ClassName, SUM(Score) AS TotalScore FROM ScoreInfo GROUP BY StudentID, StudentName, ClassName ) SELECT StudentName, ClassName, TotalScore, DENSE_RANK() OVER (PARTITION BY ClassName ORDER BY TotalScore DESC) AS TotalRank FROM TotalScore ORDER BY ClassName, TotalRank;结果StudentNameClassNameTotalScoreTotalRank张四1班256.01王五1班256.01李四1班256.01赵六2班252.01钱七2班243.02这里又出现一个有意思的情况三个人的总分都是256928876、859180、927985DENSE_RANK让三个人并列第1下一名直接是第2。实际报表里这种多名并列很常见DENSE_RANK在这种场景下最友好。如果用ROW_NUMBER就会随机决定谁是第1、谁是第2、谁是第3反而没有意义。5.4 场景三取出每个班各科前三名这是最典型的分组TopN需求逻辑上分两步先用ROW_NUMBER生成组内序号再过滤掉序号大于3的行。因为窗口函数的结果不能直接用在WHERE里所以必须套一层派生表或CTE。WITH RankedScores AS ( SELECT StudentName, ClassName, SubjectName, Score, ROW_NUMBER() OVER (PARTITION BY ClassName, SubjectName ORDER BY Score DESC) AS RN FROM ScoreInfo ) SELECT StudentName, ClassName, SubjectName, Score, RN FROM RankedScores WHERE RN 3 ORDER BY ClassName, SubjectName, RN;这个SQL里PARTITION BY ClassName, SubjectName的作用是每个班级的每个科目都是一个独立的窗口每个窗口内独立排1、2、3名。这就是前面提过的多字段分组的实际用途。5.5 场景四查看每个学生在班里的名次及与前一名差距需求升级了不只要名次还要看跟前一名的分差。SELECT StudentName, ClassName, Score, DENSE_RANK() OVER (PARTITION BY ClassName ORDER BY Score DESC) AS RankNum, LAG(Score) OVER (PARTITION BY ClassName ORDER BY Score DESC) AS PrevScore, Score - LAG(Score) OVER (PARTITION BY ClassName ORDER BY Score DESC) AS GapToPrev FROM ScoreInfo WHERE SubjectName 数学 ORDER BY ClassName, RankNum;结果里第1名的PrevScore是NULLGapToPrev也是NULL应用层显示时要注意处理别把NULL直接显示成0什么的。这个SQL在学情分析里非常实用老师看一眼就知道这个孩子离上一名还差几分。6. 实战中的五个高频坑位与排查技巧partition by看着简单实际跑起来翻车的次数可不少。这里把我踩过的坑和排查思路完整列出来都是血泪教训。6.1 坑一窗口函数内部ORDER BY写错方向导致排名颠倒这是最基础的坑也是最容易犯的。RANK/ROW_NUMBER/DENSE_RANK后面必须跟着明确的排序条件。成绩排名要求高分在前一定要ORDER BY Score DESC。如果你写成ASC那排名就变成最低分第1名。我见过不止一次有人在ORDER BY里漏了DESC排出来的名次正好倒挂。排查思路很简单先取一个班级的数据人工核对一下看最高分是不是第1名。如果最高分排到最后一名排序方向必然错了。这种问题一条ORDER BY ClassName, Score DESC的结果集一眼就能看出来。6.2 坑二直接在WHERE里过滤窗口函数结果直接报错SQL的语法决定了WHERE子句在窗口函数计算之前执行所以你不能写WHERE ROW_NUMBER() OVER(...) 1。正确的做法是把窗口函数包在子查询或CTE里再在外面用WHERE过滤。我自己早期就犯过这个错写的SQL直接报窗口函数不允许出现在WHERE子句中一度怀疑是版本问题。实际上这属于逻辑顺序问题。以后凡是遇到要在窗口函数结果上做过滤的一律先套CTEWITH Ranked AS ( SELECT ..., ROW_NUMBER() OVER(...) AS RN FROM ... ) SELECT * FROM Ranked WHERE RN 3;6.3 坑三PARTITION BY列粒度过细导致窗口失去意义PARTITION BY的粒度直接决定窗口的范围。你按学生分窗口就只能看单个学生内部的排名你按班级分窗口才能看班级排名。有一次我帮同事排查SQL他PARTITION BY写的是StudentName结果排名每个学生都是第1他愣是找不出原因。原因很简单每个学生的窗口里就只有自己的数据排名自然是1。这种坑最大的特点是结果看起来对又好像不对。排查方法也简单SELECT DISTINCT查一下PARTITION BY用的列到底有多少个不同的值确认粒度和需求匹配。6.4 坑四DISTINCT搭配窗口函数结果出现诡异空白有段时间我图省事在SELECT里同时写了DISTINCT和窗口函数结果SQL直接报错或者输出结果莫名其妙少了行。原因是DISTINCT会先去重再去计算窗口函数两个逻辑叠加在一起经常导致窗口函数的计算范围不是你想要的那个。正确做法是先算窗口函数再去重。也就是说窗口函数放在子查询里外面的SELECT再用DISTINCT。示例SELECT DISTINCT StudentName, ClassName, TotalScore FROM ( SELECT StudentName, ClassName, SUM(Score) OVER (PARTITION BY StudentID, ClassName) AS TotalScore FROM ScoreInfo ) AS T;6.5 坑五NULL成绩参与了排名导致结果不符合预期成绩表里经常有缺考、缓考的情况成绩列是NULL。如果你直接ORDER BY Score DESC在MSSQL的默认排序规则里NULL会排在最前面或者最后面取决于排序方向这会干扰排名。比如缺考的NULL成绩如果参与排序可能直接排到第1名前面这绝对不能接受。处理办法在窗口函数里显式处理NULL比如把NULL成绩换成0再参与排名DENSE_RANK() OVER (PARTITION BY ClassName ORDER BY ISNULL(Score, 0) DESC) AS RankNum或者干脆在WHERE阶段把缺考的记录先排除掉让缺考学生不参与排名单独在报表里标记为缺考。7. 性能观察与索引建议排名SQL跑不快的原因写对了SQL还得跑得快才行。成绩排名这种需求数据量一大几万行、几十万行开窗函数的性能差异马上就体现出来了。7.1 排序是最大的开销开窗函数里的ORDER BY必然触发排序操作。PARTITION BY的分组也涉及哈希或排序。如果表上没有合适的索引SQL Server就得在内存里临时排序数据量一大tempdb压力飙升查询慢得让人怀疑人生。所以我建议在PARTITION BY和ORDER BY涉及的列上建立组合索引。比如对于按班级、科目排名的SQL在ScoreInfo表上建一个如下索引CREATE INDEX IX_ScoreInfo_Class_Subject ON ScoreInfo(ClassName, SubjectName, Score DESC);这个索引能让SQL Server直接通过索引顺序读取数据省掉排序这一步性能提升非常明显。7.2 避免不必要的PARTITION BYPARTITION BY不是越多越好。有些人图省事把可能用到的分组维度全塞进PARTITION BY结果窗口数量爆炸每个窗口里的数据少得可怜计算效率反而下降。每当你要加一个分组维度时先问自己这个维度真的需要吗比如按班级排名的场景PARTITION BY ClassName就够用你把SubjectName也塞进去反而把窗口切碎。7.3 大结果集配合分页的思路排名结果往往要分页展示。给成绩排名分批导出的时候我习惯用OFFSET FETCHORDER BY ClassName, RankNum OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY;再配合前面的排名CTE一次只取100条避免一次性把几十万行排名结果全部拉回应用层那样内存和网络都顶不住。8. 从一张综合报表看MSSQL开窗函数的真实威力最后用一个综合案例把所有功能串起来。业务需求是生成一份期末考试成绩个人总览报表每个学生一行包含总分、班级总分排名、各科最高分对比、离最高分差距、班级平均分对比还要算出每个学生的总分在年级的百分位。SQL写出来是这个样子WITH StudentTotal AS ( SELECT StudentID, StudentName, ClassName, SUM(Score) AS TotalScore FROM ScoreInfo GROUP BY StudentID, StudentName, ClassName ), Ranked AS ( SELECT StudentName, ClassName, TotalScore, DENSE_RANK() OVER (PARTITION BY ClassName ORDER BY TotalScore DESC) AS ClassRank, CUME_DIST() OVER (ORDER BY TotalScore DESC) AS CumPercent, FIRST_VALUE(TotalScore) OVER (PARTITION BY ClassName ORDER BY TotalScore DESC) AS ClassTopScore, AVG(TotalScore) OVER (PARTITION BY ClassName) AS ClassAvgScore FROM StudentTotal ) SELECT StudentName, ClassName, TotalScore, ClassRank, CAST(CumPercent * 100 AS DECIMAL(5,1)) AS PercentileRank, ClassTopScore, TotalScore - ClassTopScore AS GapToTop, ClassAvgScore, TotalScore - ClassAvgScore AS DiffFromAvg FROM Ranked ORDER BY ClassName, ClassRank;这个SQL一口气完成了每班总分排名全年级百分位与班级最高分的差距与班级平均分的差距如果不用开窗函数这个报表至少得写四个子查询再合并还不一定写得对。开窗函数的优势在这种多维度数据分析场景下体现得淋漓尽致。再说一个我自己的使用体会很多人觉得开窗函数难是因为没有建立起窗口视角。把一张表想象成一面墙GROUP BY是把墙拆成一堆砖头的横截面而PARTITION BY是在墙上开窗户——透过每扇窗户都能看到一组数据但墙本身还在。想通了这一点再复杂的排名需求也就那么回事。这篇实战从最基础的GROUP BY痛点讲到排名函数的取舍再到聚合、极值、前后值对比最后落到完整报表和性能排查算是一个相对完整的学习路径。如果你手头正好有一份成绩表建议直接拿这些SQL去跑一遍亲手验证一遍每个函数的行为差异——尤其是那个带并列的RANK和DENSE_RANK跑一次比看十篇文章都记得牢。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

MindManager 2026 安装初始化报错排查与高效使用指南 2026/10/2 16:53:50

MindManager 2026 安装初始化报错排查与高效使用指南

很多刚接触思维导图的朋友问我,2026年如果只想选一款桌面端思维导图工具,到底该不该直接上 MindManager?我的回答一直是:如果你的工作流里充斥着复杂的项目拆解、会议纪要和知识体系整理,那它依然是目前逻辑承载能力最…

阅读更多 →
质量工程师的完整工具地图:从测试设计到CI/CD落地 2026/10/2 16:53:50

质量工程师的完整工具地图:从测试设计到CI/CD落地

做质量工程师这些年,我最大的感触是:这个岗位看着拼的是工具熟练度,实际上拼的是对工具背后逻辑的理解。我见过有人把JMeter的线程数调得很溜,却连一个像样的性能测试计划都写不出来;也见过团队把JIRA流程建得比需求还…

阅读更多 →
PHP上传几MB图片为什么CPU飙到100%?从图片尺寸到异步处理的实战 2026/10/2 16:53:43

PHP上传几MB图片为什么CPU飙到100%?从图片尺寸到异步处理的实战

最近维护一个 PHP 图片上传功能时,遇到一个比较奇怪的问题: 用户上传一张普通图片,文件大小只有几 MB,但是服务器 CPU 很快就升到了 100%。 与此同时,PHP-FPM 出现大量慢请求: PHP请求耗时 8 秒 PHP请求耗时…

阅读更多 →
IMU标定避坑指南:从零偏建模到壳体失准角,飞控落地的关键一步 2026/10/2 16:53:37

IMU标定避坑指南:从零偏建模到壳体失准角,飞控落地的关键一步

前阵子帮一个做飞控的团队排查落地漂移问题,我问他们的IMU标定是怎么做的。对方说得很顺畅:模块平放一分钟取平均值当零偏,六个方向各停十秒算比例因子,然后直接装到无人机上。我一听就知道问题出在哪了——这套流程只能算"测…

阅读更多 →
STM32H743VIT6封装与系统边界深度解析 2026/10/2 16:53:37

STM32H743VIT6封装与系统边界深度解析

1. 为什么“STM32H743VIT6采购复核”这件事,90%的工程师都踩在同一个坑里?你手头正赶一个工业边缘网关项目,BOM表里赫然写着“STM32H743VIT6 2”,采购同事发来确认邮件:“型号已锁定,交期4周,是…

阅读更多 →
ESP32 接大模型不是 AI 硬件:8 个工程陷阱与量产级解决方案 2026/10/2 16:53:37

ESP32 接大模型不是 AI 硬件:8 个工程陷阱与量产级解决方案

上个月我把一个 ESP32-S3 语音助手接上了大模型 API,朋友看了一眼说:这不就是 AI 硬件了吗?我笑了笑没接话。因为被他忽略掉的那一长串东西——内存、算力、模型转换、云端链路、功耗、实时性、稳定性、安全——才是真正让人掉头发的地方。网…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉