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
我有一个 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