SQL 查询是否所有 sub-parts 都存在

SQL query to find whether all sub-parts exist

我有一个 table(课程)与另一个 table(SessionAttendance)有 one-to-many 关系。我需要知道很多条记录中的全部是否存在。

部分问题是我不确定如何很好地解释这个问题,所以很难搜索:)

例子

这是我正在尝试做的一个简化示例。我找到了一种(sub-optimal?)方法来确定 一个学生 是否参加了全部 session。但是,不确定如何将其扩展到一般搜索

场景

一门课程有多个 session。给定 session.

的出勤记录

对于每个学生和课程,是否全部 class 都参加了,最后一次 class 参加的日期是什么时候?

一名学生

这是我为一个学生想到的。当任何 NULL 字段存在时,不知道会 return NULL 的任何聚合,所以我最终嵌套了 CASE 语句:- /(似乎不会影响性能)

SELECT
    Course.Id,
    MAX(SessionAttendance.TakenOn) as TakenOn,
    CASE
        WHEN SUM(CASE WHEN SessionAttendance.Id is null THEN 1 ELSE 0 END) > 0 THEN 0
        ELSE 1
    END as AttendedAllSessions    -- Is there a better way?
FROM Course
LEFT JOIN SessionAttendance
    ON Course.Id = SessionAttendance.CourseId
WHERE SessionAttendance.StudentId = 42
GROUP BY Course.Id

所有学生

这是我不确定如何前进的地方。 StudentId 仅存在于 Attendance table 中。无法将其添加到 GROUP BY,因为 NULLs(来自 LEFT JOIN)最终会一起

SELECT
    SessionAttendance.StudentId, -- NOPE: all NULL Attendance gets combined
    Course.Id,
    MAX(SessionAttendance.TakenOn) as TakenOn,
    CASE
        WHEN SUM(CASE WHEN SessionAttendance.Id is null THEN 1 ELSE 0 END) > 0 THEN 0
        ELSE 1
    END as AttendedAllSessions
FROM Course
LEFT JOIN SessionAttendance
    ON Course.Id = SessionAttendance.CourseId
GROUP BY SessionAttendance.StudentId, Course.Id

Table 定义

CREATE TABLE Course (
    Id INT IDENTITY,
    Name VARCHAR(64)
);

CREATE TABLE SessionAttendance (
    Id INT IDENTITY,
    CourseId INT,   -- FK to Course.Id
    StudentId INT,  -- FK to Student.Id
    TakenOn DATETIME
);

CREATE TABLE Student (
    Id INT IDENTITY,
    Name VARCHAR(64)
);

这个查询:

select 
    c.Id as CourseId, c.Name as CourseName, cs.SessionDate, 
    s.Id as StudentId, s.Name as StudentName, 
    case when a.Id is null then 0 else 1 end as Attended
from (
    select distinct CourseId, TakenOn [SessionDate] from SessionAttendance 
) cs
join Course c on c.Id=cs.CourseId
cross join Student s
left join SessionAttendance a 
    on a.CourseId=cs.CourseId and a.StudentId=s.Id and a.TakenOn=cs.SessionDate
order by c.Id, s.Id, cs.SessionDate

获取所有课程中所有课程的列表以及所有学生的列表,以及显示每个学生是否参加每个课程的标志:

CourseId    CourseName  SessionDate                 StudentId   StudentName Attended
1           CS101       2015-01-01 00:00:00.000     1           me          1
1           CS101       2015-01-02 00:00:00.000     1           me          1
1           CS101       2015-01-03 00:00:00.000     1           me          1
1           CS101       2015-01-01 00:00:00.000     2           you         1
1           CS101       2015-01-02 00:00:00.000     2           you         0
1           CS101       2015-01-03 00:00:00.000     2           you         0
2           CS105       2015-01-01 00:00:00.000     1           me          0
2           CS105       2015-01-01 00:00:00.000     2           you         1

因此您可以在该查询上添加一些聚合,以获得:

select 
    c.Id as CourseId, c.Name as CourseName,
    s.Id as StudentId, s.Name as StudentName, 
    case 
        when sum(case when a.Id is null then 1 else 0 end) > 0 then 0 
        else 1 
    end as AttendedAllSessions,
    max(a.TakenOn) as LastSessionAttended
from (
    select distinct CourseId, TakenOn [SessionDate] from SessionAttendance 
) cs
join Course c on c.Id=cs.CourseId
cross join Student s
left join SessionAttendance a on 
    a.CourseId=cs.CourseId and a.StudentId=s.Id and a.TakenOn=cs.SessionDate
group by c.Id, c.Name, s.Id, s.Name

结果如下:

CourseId    CourseName  StudentId   StudentName AttendedAllSessions LastSessionAttended
1           CS101       1           me          1                   2015-01-03 00:00:00.000
1           CS101       2           you         0                   2015-01-01 00:00:00.000
2           CS105       1           me          0                   NULL
2           CS105       2           you         1                   2015-01-01 00:00:00.000

这是 fiddle.

如果您只是将出勤记录数与可用课程总时数进行比较,结果如何:

SELECT
    StudentId,
    CourseId,
    MAX(TakenOn) as TakenOn,
    case when Count(CourseId) = 
            (select COUNT(distinct TakenOn) 
            FROM SessionAttendance SessionAttendanceCount
            where SessionAttendanceCount.CourseId = SessionAttendance.CourseId
            ) then
        1
    else
        0
    end AttendedAllSessions
FROM SessionAttendance
GROUP BY StudentId, CourseId