MySQL查询中double类型数据导致排名显示为0的问题排查
问题原因及解决方案
核心问题1:逻辑不匹配
你写的SQL里,子查询的WHERE条件是assessement_id=1 AND student_stream=1,但外层查询的是assessement_id=2 AND student_stream=2的学生数据。也就是说,你拿评估1、流1的平均分列表,去匹配评估2、流2的学生平均分,两者完全不是同一组数据,自然匹配不到,返回0。这是最严重的逻辑错误,不管字段类型是整数还是浮点型,这个逻辑都不对,只是整数时可能碰巧有相同数值才没暴露问题。
核心问题2:浮点型精度的隐式转换问题
FIND_IN_SET函数的两个参数都要求是字符串类型。当字段是double或float时,MySQL会自动把浮点值转换成字符串,但浮点型的二进制存储本身存在精度损耗,比如89.9可能被存储为89.89999999999999,而GROUP_CONCAT生成的字符串里的数值可能是另一种精度表现(比如89.9),导致两者字符串不相等,FIND_IN_SET找不到匹配项,返回0。整数类型没有这个问题,因为整数转字符串不会有精度丢失。
修复方案
方案1:修正逻辑+处理浮点精度
首先把子查询的WHERE条件和外层保持一致,确保是同一组数据的平均分列表;然后用ROUND函数统一浮点值的小数位数,避免精度差异:
SELECT apr.student_id, apr.student_average, FIND_IN_SET( ROUND(apr.student_average, 2), (SELECT GROUP_CONCAT(ROUND(apr_inner.student_average, 2) ORDER BY apr_inner.student_average DESC) FROM assessement_progress_reports apr_inner WHERE apr_inner.assessement_id = apr.assessement_id AND apr_inner.student_stream = apr.student_stream) ) AS student_position FROM assessement_progress_reports apr WHERE apr.assessement_id = 2 AND apr.student_id = 123 AND apr.student_stream = 2;
这里用ROUND(...,2)保留两位小数,你可以根据实际需求调整小数位数。
方案2:使用窗口函数(推荐)
MySQL 8.0及以上版本支持窗口函数,用RANK()或DENSE_RANK()来排名更可靠,完全避免浮点精度和字符串匹配的问题:
SELECT student_id, student_average, RANK() OVER (PARTITION BY assessement_id, student_stream ORDER BY student_average DESC) AS student_position FROM assessement_progress_reports WHERE assessement_id = 2 AND student_id = 123 AND student_stream = 2;
RANK():如果有相同分数,会跳过后续排名(比如两个第1名,下一个是第3名)DENSE_RANK():相同分数不会跳过排名(两个第1名,下一个是第2名)
根据你的需求选择即可。
内容的提问来源于stack exchange,提问作者Lusit
相关产品推荐
相关产品推荐

