在 SQL Server 中进行数据统计时,按分组计算平均值并对结果排序是一项非常普遍的需求。比如统计各部门的平均薪资、各课程的平均成绩、各区域的平均销售额,最终还要按照平均值从高到低或从低到高展示。完成这类任务的核心在于掌握 GROUP BY 分组子句、AVG 聚合函数以及 ORDER BY 排序子句的搭配方法。三者的逻辑顺序是先分组、再聚合、最后排序,缺少任何一环都会导致结果不符合预期。

一、GROUP BY 与 AVG 的基础组合
要计算每个分组的平均值,需要使用 GROUP BY 子句指定分组列,SELECT 列表中只能出现分组列和聚合表达式。AVG 函数负责对数值列求平均,如果该列中存在 NULL 值,NULL 会被自动忽略,只对非空值进行求和与计数。这种忽略 NULL 的行为在数据质量不稳定时需要特别注意,因为最终平均值可能基于不同的有效记录数。
ORDER BY 的位置必须放在 GROUP BY 之后,它作用于聚合完成后的结果集。别名 avg_salary 可以在 ORDER BY 中直接引用,SQL Server 允许在排序阶段使用 SELECT 阶段定义的列别名。如果违背顺序,例如在 GROUP BY 之前就写 ORDER BY,数据库引擎会抛出语法错误,因为排序无法针对尚未生成的聚合结果执行。
-- 按部门统计平均薪资,并按平均薪资降序排列
SELECT
department_id,
AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id
ORDER BY avg_salary DESC;
二、使用 HAVING 筛选聚合结果
如果只需要保留平均工资超过某个阈值的部门,就要使用 HAVING 子句。HAVING 与 WHERE 的本质区别在于执行时机:WHERE 在分组前过滤原始行,而 HAVING 在分组后过滤聚合结果。因此在 WHERE 中写 AVG(salary) 之类的聚合函数是错误的,因为 WHERE 执行时聚合计算还未发生。
HAVING 可以使用 SELECT 中定义的别名,也可以直接使用 AVG(salary) 表达式。将 HAVING 与 ORDER BY 搭配时,顺序应为 GROUP BY、HAVING、ORDER BY。下面的示例筛选出平均薪资大于 5000 的部门,并按平均值升序排列,适合从低到高观察数据分布。
-- 筛选平均薪资大于5000的部门,并按平均值升序排列
SELECT
department_id,
AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id
HAVING AVG(salary) > 5000
ORDER BY avg_salary ASC;
三、多列排序与列的别名
在真实业务场景中,单一排序条件往往不够。例如课程平均分相同的课程还需要按选课人数排序,以避免展示顺序随机变化。ORDER BY 支持多个排序字段,各字段之间用逗号分隔,每个字段都可以独立指定 ASC 或 DESC 方向。使用 SELECT 中定义的列别名可以让排序语句更加易读。
多列排序的执行顺序是从左到右,第一个字段优先级最高,后面的字段只在前面字段出现相同值时生效。在排行榜、报表或数据导出等场景中,合理的次级排序能确保输出稳定且有业务意义。下面示例按课程平均分降序排列,平均分相同时按选课人数升序排列。
-- 统计课程平均分和选课人数,使用多列排序
SELECT
course_id,
AVG(score) AS avg_score,
COUNT(*) AS student_count
FROM course_score
GROUP BY course_id
ORDER BY avg_score DESC, student_count ASC;
四、常见错误及排查思路
初学者经常遇到的一个问题是,SELECT 中出现了既不在 GROUP BY 中、也不是聚合函数的列,或者 ORDER BY 引用了这样的列。SQL Server 会提示该列在聚合查询中无效,因为聚合后的结果集里该列没有唯一值。正确做法有两种:将该列加入 GROUP BY,或者使用某种聚合函数处理该列后再参与排序。
另一个典型错误是用 WHERE 过滤平均值,例如写出 WHERE AVG(salary) > 5000。这种写法会直接报错,因为聚合函数的计算结果在 WHERE 阶段不存在。正确的过滤工具是 HAVING。此外,如果列别名包含空格或特殊字符,需要将别名放入方括号中,例如 ORDER BY [average salary],否则解析器无法识别完整别名。
-- 错误写法参考:不要在 WHERE 中使用聚合函数
-- WHERE AVG(salary) > 5000
-- 该写法会触发语法错误,应改用 HAVING 子句进行过滤
SELECT
department_id,
AVG(salary) AS [average salary]
FROM employees
GROUP BY department_id
HAVING AVG(salary) > 5000
ORDER BY [average salary];
五、性能优化与索引策略
当表数据量较大时,分组与排序操作可能触发哈希匹配、排序运算符,消耗大量临时内存和 CPU 资源。为了减少这些开销,可以在经常用于分组和排序的列上创建复合索引。例如在 employees 表的 department_id 和 salary 上建立索引后,数据库引擎有可能按有序方式读取数据,降低显式排序的需求。
对于报表类查询,如果平均值计算频繁且实时性要求不高,可以考虑定期将明细数据汇总到单独的统计表,查询时直接读取预计算的平均值。这种方式以额外的存储空间换取更快的查询速度,配合 ORDER BY 能够稳定输出排序结果,避免每次都做全表扫描和聚合计算。
-- 建立部门编号与薪资的复合索引,辅助分组与排序 CREATE INDEX idx_department_salary ON employees(department_id, salary);
综上所述,SQL Server 中查询记录平均值并排序的关键在于明确 SQL 子句的执行顺序:先通过 GROUP BY 分组,再用 AVG 计算平均值,必要的话用 HAVING 筛选聚合结果,最后用 ORDER BY 进行排序。开发者在编写查询时还应注意 WHERE 与 HAVING 的职责边界、多列排序的优先级以及索引对性能的影响。掌握这些技巧后,无论是简单的部门统计还是复杂报表,都能写出结构清晰、结果准确且性能良好的聚合查询。
SQL_ServerAVG函数ORDER_BY修改时间:2026-08-02 20:18:16