栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

MySQL 多表关联统计,无数据的记录正常显示并补0

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

MySQL 多表关联统计,无数据的记录正常显示并补0

需求

统计course 表中各课程所对应的课次(lesson表)和学生(student表)数量

数据库表

以下3个表中,student 和 lesson 表中的course_id 字段,都关联至course 表中的id字段
course表

idname
1VB
2Python
3Java

lesson表

idnamecourse_id
105291
206121
308202
408032

student表

idnamecourse_id
1aa1
2bb2
3cc1
实现过程 尝试1 SQL1(有问题,未完全统计)
SELECt
    c.id cId,c.`name` cName,count( DISTINCT s.id ) stuNum,count( DISTINCT l.id ) lessonNum 
FROM
    student s
    JOIN course c ON s.course_id = c.id
    JOIN lesson l ON l.course_id = c.id 
GROUP BY
    c.id;
查询结果
cIdcNamesNumlessNum
1VB22
2Python12
结果(问题)分析

course 表有3条记录,对应3种课程,但是此处的统计结果中没有 course.name=Java 的记录。
原因在于Java 课程没有对应的student和lesson,所以在联表的时候,没有将该课程记录进行统计。
预期结果应该是列出全部课程,但是像此处的Java 课程没有对应的student和lesson,那么就将统计数量记为0 。

尝试2 思路

最外层查询course 表,这样可以确保course 表中的记录都在。然后Left Join一个子查询,这个子查询其实就是前面那条查询sql。
子查询的结果依然只有VB和Python两条,但因为整个子查询的结果集Left Join到course表,所以统计结果中course表中的记录是完整的。

SQL2(可用)
SELECt
    c.id cId,c.`name` cName,IFNULL( d.stuCount, 0 ) stuNum,IFNULL( d.lessonCount, 0 ) lessonNum 
FROM
    course c
    LEFT JOIN (
    SELECt
        c.id cId,c.`name` cName,count( DISTINCT s.id ) stuCount,count( DISTINCT l.id ) lessonCount 
    FROM
        course c
        JOIN student s ON s.course_id = c.id
        JOIN lesson l ON l.course_id = c.id 
    GROUP BY
        c.id 
    ) d ON d.cId = c.id;
查询结果
cIdcNamesNumlessNum
1VB22
2Python12
3Java00

符合预期的结果,但依然可以优化。

尝试3(推荐) 思路

前面写的SQL2,用左联的方式,确保左表中的记录完整,那就直接左联到底。
在SQL1的基础上,调整下3个表的关联顺序和关联方式。
course表在最左,后面的student、lesson表依次左联,也可以有同样的效果,这样就可以把子查询给省掉了。

SQL3
SELECt
    c.id cId,c.`name` cName,count( DISTINCT s.id ) stuNum,count( DISTINCT l.id ) lessonNum 
FROM
    course c
    LEFT JOIN student s ON s.course_id = c.id
    LEFT JOIN lesson l ON l.course_id = c.id 
GROUP BY
    c.id;
查询结果
cIdcNamesNumlessNum
1VB22
2Python12
3Java00

结果符合预期。

后续SQL对比

分别给SQL2和SQL3的sql语句前加上explain,就可以查看sql执行的情况

EXPLAIN SELECT cId,cName...... 
SQL2
idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra
1PRIMARYcALL3100
1PRIMARYref4c.id2100
2DERIVEDsindexFKdfypyqtj9kcxm99sFKdfypyqtj9kcxm99s53100Using where; Using index; Using temporary; Using filesort
2DERIVEDceq_refPRIMARYPRIMARY4course_id1100
2DERIVEDlrefFKjs3c7sk5lc7s807FKjs3c7sk5lc7s8075course_id2100Using index
SQL3
idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra
1SIMPLEcindexPRIMARYPRIMARY43100
1SIMPLEsrefFKdfypyqtj9kcxm99sFKdfypyqtj9kcxm99s5c.id1100Using index
1SIMPLElrefFKjs3c7sk5lc7s807FKjs3c7sk5lc7s8075c.id2100Using index
分析

很容易发现,SQL2查询步骤更多,而且各种查询的类型(type字段值)都快凑齐了,尤其是第一个为全表扫描,虽然扫描的是course表,里面数据不会特别多,影响不算大,但如果在其他场景就不一定适用了。
再看Extra里面,SQL2的查询中除了正常索引外,还生成了临时表(Using temporary)和文件排序(Using filesort),这俩也会增加查询的耗时,所以一般可以的话也得把它俩优化掉。
对比下来,SQL3没啥毛病,查询都用上了索引,type都是ref或index级别的,也还行。

结论

在文中场景下,SQL3是比较推荐的写法。
可能还有更高效、优雅的实现方式,欢迎大家一起交流讨论

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/320071.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号