SQL分组排序需求:按组内首个Name排序组,组内顺序保持不变
解决分组按首字段排序且组内保序的SQL问题
首先说下你原SQL的问题:GROUP BY \Serial no.`的作用是**聚合每组数据**,只会返回每组的一条记录(具体返回哪条取决于数据库配置),这和你需要保留每组所有记录的需求完全不符;而且ORDER BY Name`是对所有记录单独排序,不是按组的首个Name来排序整个组。
核心思路
要实现你的需求,需要两步:
- 先给每个
Serial no.组标记出它的首个Name值(也就是组内第一条记录的Name) - 然后按这个首个Name值对整个组排序,同时组内保持原有的记录顺序
具体SQL实现
情况1:表有明确的原顺序字段(比如自增ID id)
如果你的表有能确定数据插入顺序的字段(比如自增主键id),用窗口函数是最简洁的方式:
SELECT `Serial no.`, Name FROM ( SELECT `Serial no.`, Name, -- 获取当前组的第一个Name值 FIRST_VALUE(Name) OVER (PARTITION BY `Serial no.` ORDER BY id) AS group_first_name, -- 标记组内的行号,用来保留原顺序 ROW_NUMBER() OVER (PARTITION BY `Serial no.` ORDER BY id) AS group_row_num FROM tablename ) AS temp ORDER BY group_first_name, -- 按组的首个Name排序 `Serial no.`, -- 确保同组记录在一起(可选) group_row_num; -- 组内按原顺序排列
情况2:表没有明确的原顺序字段(支持窗口函数的数据库:MySQL 8.0+/PostgreSQL/SQL Server)
如果没有自增ID这类字段,我们可以用临时行号模拟原顺序(注意:如果表没有主键,数据库默认顺序是不确定的,最好还是有明确的排序字段):
SELECT `Serial no.`, Name FROM ( SELECT `Serial no.`, Name, FIRST_VALUE(Name) OVER (PARTITION BY `Serial no.` ORDER BY (SELECT NULL)) AS group_first_name, ROW_NUMBER() OVER (PARTITION BY `Serial no.` ORDER BY (SELECT NULL)) AS group_row_num FROM tablename ) AS temp ORDER BY group_first_name, `Serial no.`, group_row_num;
情况3:MySQL 5.x(不支持窗口函数)
用用户变量来实现分组标记和排序:
SELECT `Serial no.`, Name FROM ( SELECT t.*, @first_name := CASE WHEN @current_serial != `Serial no.` THEN Name ELSE @first_name END AS group_first_name, @row_num := CASE WHEN @current_serial != `Serial no.` THEN 1 ELSE @row_num + 1 END AS group_row_num, @current_serial := `Serial no.` FROM tablename t, (SELECT @current_serial := '', @first_name := '', @row_num := 0) AS vars -- 这里要指定原顺序,比如按主键id排序 ORDER BY `Serial no.`, id ) AS temp ORDER BY group_first_name, `Serial no.`, group_row_num;
验证结果
用你提供的示例数据执行上述SQL后,会得到完全符合需求的结果:
Serial no. Name 2 Aa 2 Z 1 A 1 B 3 D 3 A 3 C
内容的提问来源于stack exchange,提问作者Ashutosh Rawat
相关产品推荐
相关产品推荐

