SQL查询问题:如何列出所有学生指定年月的请假天数(含无请假)
解决按年月列出所有学生请假天数的SQL问题
我来帮你搞定这个查询问题!首先先明确你的表结构和数据,然后分析原查询的问题,再给出正确的解决方案。
你的表结构与数据
student表
| ID | Name |
|---|---|
| 001 | Bob |
| 002 | Jack |
| 003 | Mary |
attendance表
| ID | StentID | OffDays | Month | Year |
|---|---|---|---|---|
| 1 | 001 | 2 | jan | 2018 |
| 2 | 002 | 4 | feb | 2018 |
| 3 | 002 | 1 | mar | 2017 |
| 4 | 003 | 2 | Apr | 2018 |
原查询的问题分析
你原来的查询有两个关键问题导致无法显示无请假记录的学生:
- 拼写错误:表名
StudentAttendance应该是attendance,关联字段Ste应该是StentID,还有att.Days应该对应表中的OffDays字段; - 核心逻辑缺陷:单纯的左连接只能关联已有请假记录的学生和年月,但如果某个学生在某个年月没有请假,或者某个年月没有任何学生请假,这些组合就不会出现在结果里——我们需要先生成所有需要统计的学生+年月组合,再去匹配请假记录。
正确的SQL查询方案
要实现“按年月列出所有学生的请假天数(无请假则显示0)”,我们需要分两步走:
- 先提取所有需要统计的年月组合(从已有请假记录中取不重复的年月,或者手动指定范围);
- 将学生表和年月组合做交叉连接,得到每个学生对应每个年月的基础记录,再左连接到请假表补全请假天数。
基于已有请假记录的年月查询
这个方案会覆盖所有在attendance表中出现过的年月,确保每个学生在这些年月都有记录:
SELECT s.Name, ym.Year, ym.Month, ISNULL(a.OffDays, 0) AS OffDays FROM student s -- 交叉连接生成所有学生+年月的组合 CROSS JOIN (SELECT DISTINCT Year, Month FROM attendance) ym -- 左连接请假表,匹配对应学生对应年月的记录 LEFT JOIN attendance a ON s.ID = a.StentID AND ym.Year = a.Year AND ym.Month = a.Month -- 按年月和学生姓名排序,方便查看 ORDER BY ym.Year, ym.Month, s.Name;
执行这个查询后,你会得到这样的结果:
- 有请假记录的学生(比如Bob在2018年1月)会显示对应的
OffDays数值; - 无请假记录的学生(比如Mary在2017年3月)会显示
OffDays = 0。
扩展:指定自定义年月范围
如果你的统计范围不是只基于已有请假记录,而是比如要统计2017年1月到2018年12月的所有月份,可以用递归CTE生成完整的年月序列(以SQL Server为例):
-- 递归生成2017年1月到2018年12月的所有年月 WITH DateRange AS ( SELECT 2017 AS Year, 'jan' AS Month UNION ALL SELECT -- 12月后年份+1 CASE WHEN Month = 'dec' THEN Year + 1 ELSE Year END, -- 月份逐月切换 CASE Month WHEN 'jan' THEN 'feb' WHEN 'feb' THEN 'mar' WHEN 'mar' THEN 'apr' WHEN 'apr' THEN 'may' WHEN 'may' THEN 'jun' WHEN 'jun' THEN 'jul' WHEN 'jul' THEN 'aug' WHEN 'aug' THEN 'sep' WHEN 'sep' THEN 'oct' WHEN 'oct' THEN 'nov' WHEN 'nov' THEN 'dec' WHEN 'dec' THEN 'jan' END FROM DateRange -- 终止条件:到2018年12月为止 WHERE NOT (Year = 2018 AND Month = 'dec') ) SELECT s.Name, dr.Year, dr.Month, ISNULL(a.OffDays, 0) AS OffDays FROM student s CROSS JOIN DateRange dr LEFT JOIN attendance a ON s.ID = a.StentID AND dr.Year = a.Year AND dr.Month = a.Month ORDER BY dr.Year, dr.Month, s.Name;
内容的提问来源于stack exchange,提问作者Vahid
相关产品推荐
相关产品推荐

