SQL分组统计前去重 优化嵌套GROUP BY实现方案
去重统计优化方案
你不需要写两层GROUP BY,直接用多列参数的COUNT(DISTINCT)聚合就能一步得到结果,写法如下:
SELECT Test AS TestName, COUNT(DISTINCT Patient, Test, Value) AS Result FROM patient_test GROUP BY Test;
这个写法的执行逻辑是:按检测项分组聚合时,自动对「患者+检测项+检测值」三个维度的重复组合去重计数,数据库引擎会在单次聚合扫描中完成所有计算,比嵌套子查询的写法少一层结果集暂存的开销,执行效率更高。
不同写法的注意事项
- 你之前用的
SELECT * FROM patient_test GROUP BY Patient , Test, Value写法存在兼容性风险:在开启ONLY_FULL_GROUP_BY模式的MySQL、或PostgreSQL等严格遵循SQL标准的数据库中,SELECT子句中未出现在GROUP BY里、也未做聚合处理的字段会直接抛出语法错误,返回的未分组字段值也存在不确定性,不建议生产环境使用。 - 你提到
DISTINCT会丢失字段的问题,仅存在于单独用SELECT DISTINCT 列名返回结果的场景;把DISTINCT写在COUNT()聚合函数内部时,只影响计数的去重逻辑,不会限制外层SELECT返回其他业务字段,你可以正常查询patient_test表的其他列,配合对应聚合函数使用即可。 - 关于视图封装的效率:普通视图本身不存储数据,只是封装了SQL逻辑,查询时还是会展开执行底层语句,不会带来性能提升;如果你的数据库支持物化视图(如PostgreSQL、Oracle),提前把去重后的明细做预计算存储,才会提升高频查询的效率,但需要额外处理数据同步的开销。
扩展:保留全字段的去重明细方案
如果你后续需要用到去重后的完整明细(携带patient_test表所有业务字段),可以用窗口函数实现稳定去重,替代GROUP BY写法:
SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY Patient, Test, Value ORDER BY (SELECT 1)) AS rn FROM patient_test ) t WHERE rn = 1
这个语句会给每一组重复的「患者+检测项+检测值」记录生成行号,仅保留每组第一条数据,所有原表字段都会完整保留,不受SQL_MODE限制,基于这个明细做后续统计也不会出现字段丢失的问题。
上述统计SQL执行后会直接返回你需要的结果:
TestName | Result ---------------------- Test A | 2 Test B | 5 Test C | 3
内容的提问来源于stack exchange,提问作者Alberto Sanchez
相关产品推荐
相关产品推荐

