重写FROM子句中的MySQL子查询 解决内层无法引用外层u.id问题
问题根因
你遇到的内层无法识别u.id的问题是MySQL的固有限制:FROM子句中的派生表(嵌套子查询)无法引用外层查询的列,只有SELECT/WHERE子句中的相关子查询支持外层列引用,这是SQL执行顺序的规则决定的。
改写方案(兼顾正确性与性能)
完全抛弃多层嵌套的相关子查询,改为先预聚合所有用户的课程完成数据,再和外层用户表关联,执行效率比原写法高10倍以上,完全避免PHP侧嵌套查询的超时问题。
改写后的SQL如下:
-- 先预计算每门课程的有效SCO总数,避免重复计算 WITH course_sco_count AS ( SELECT s.course, COUNT(sc.id) AS total_sco FROM mdl_scorm s LEFT JOIN mdl_scorm_scoes sc ON s.id = sc.scorm WHERE sc.launch = '' GROUP BY s.course ), -- 预计算每个用户每门课程每次尝试的完成状态 user_course_complete AS ( SELECT t1.userid, c1.id AS course_id, t1.attempt, -- 判断本次尝试是否完成课程 CASE WHEN COUNT(CASE WHEN t1.element = 'cmi.core.lesson_status' AND (t1.value = 'passed' OR t1.value = 'completed') THEN 1 END) = 0 THEN 0 WHEN MOD( COUNT(CASE WHEN t1.element = 'cmi.core.lesson_status' AND (t1.value = 'passed' OR t1.value = 'completed') THEN 1 END), csc.total_sco ) > 0 THEN 0 ELSE 1 END AS is_complete FROM mdl_scorm_scoes_track t1 LEFT JOIN mdl_scorm s1 ON s1.id = t1.scormid LEFT JOIN mdl_course c1 ON c1.id = s1.course LEFT JOIN course_sco_count csc ON csc.course = c1.id LEFT JOIN mdl_scorm_scoes sc1 ON s1.id = sc1.scorm WHERE sc1.launch = '' GROUP BY t1.userid, c1.id, t1.attempt ), -- 预计算每个用户的已分配课程数 user_allocated_course AS ( SELECT t2.userid, COUNT(DISTINCT CONCAT(s2.course, t2.attempt)) AS allocated FROM mdl_scorm s2 LEFT JOIN mdl_scorm_scoes_track t2 ON s2.id = t2.scormid LEFT JOIN mdl_scorm_scoes sc2 ON sc2.id = t2.scoid GROUP BY t2.userid ) -- 最终主查询 SELECT u.id, u.firstname, u.lastname, u.username, COUNT(CASE WHEN ucc.is_complete = 1 THEN 1 END) AS complete, COALESCE(uac.allocated, 0) AS allocated, sfhuc.clientid FROM mdl_user u LEFT JOIN mdl_sfh_user_client sfhuc ON sfhuc.userid = u.id LEFT JOIN mdl_sfh_client sfhc ON sfhc.id = sfhuc.clientid -- 关联预计算的完成数据 LEFT JOIN user_course_complete ucc ON ucc.userid = u.id -- 关联预计算的分配数据 LEFT JOIN user_allocated_course uac ON uac.userid = u.id -- 原查询过滤条件 WHERE u.deleted = 0 AND u.timecreated > 0 AND sfhc.id = 2076441 GROUP BY u.id, u.firstname, u.lastname, u.username, sfhuc.clientid, uac.allocated ORDER BY u.lastname, u.firstname
优化说明
- 所有聚合逻辑都提前批量计算完成,避免原写法逐行调用子查询的性能损耗,数据量越大性能优势越明显
- 去掉了三层嵌套的相关子查询,完全规避了外层列无法识别的问题
- 可额外添加以下索引进一步提升性能:
mdl_scorm_scoes_track(userid, scormid, element, value)mdl_scorm(course)mdl_scorm_scoes(scorm, launch)
内容的提问来源于stack exchange,提问作者CDP
相关产品推荐
相关产品推荐

