SQL初学者,在我的大学课程中,我们有以下架构。
Student(snum:integer,sname:string,major:string,level:string,age:integer)
Class(name:string,meets_at:string,room:string,fid:integer)
已注册(snum:integer,cname:string)
Faculty(fid:integer,fname:string,deptid:integer)
其中一项练习如下:
查找所有同时参加两个课程的学生的姓名。
以下是它的SQL语句
SELECT DISTINCT S.SNAME
FROM STUDENT S
WHERE S.SNUM IN (SELECT E1.SNUM
FROM ENROLLED E1, ENROLLED E2, CLASS C1, CLASS C2
WHERE E1.SNUM = E2.SNUM AND E1.CNAME <> E2.CNAME
AND E1.CNAME = C1.NAME
AND E2.CNAME = C2.NAME AND
C1.MEETS_AT = C2.MEETS_AT);
在我的子查询中,为什么要使用关系ENROLLED和CLASS的两个副本。
答案 0 :(得分:1)
表声明两次的原因是比较同一查询中的表。
您必须拥有该表的两个副本才能将表与自身进行比较。
您的查询正在查看E1并在E2中检查它,其中StudentNumber相同但类的名称不同。
然后它查看C1表和C2表,找到类的名称......然后查看类meet_at是否相同。
您需要两个表的原因是您无法在同一查询中两次比较E1。在单个查询中,无法检查row1与同一列的其他行。
答案 1 :(得分:0)
子查询引用ENROLMENTS
表两次,因为它需要检索多次注册的学生和CLASSES
两次,因为它只需要检索已注册多个课程的学生。
考虑一个更简单的查询:
select C1.NAME, C2.NAME
from CLASS C1, CLASS C2
where C1.MEETS_AT = C2.MEETS_AT
and C1.NAME <> C2.NAME;
这将为您提供同时会面的课程列表。有必要将完整的类集与自身进行比较,寻找具有相同MEETS_AT
时间的类。最后一行排除了那些具有相同名称的类。
您的子查询会检索已注册多个同时开课的学生的学生编号列表:
SELECT E1.SNUM
FROM ENROLLED E1,
ENROLLED E2,
CLASS C1,
CLASS C2
WHERE E1.SNUM = E2.SNUM -- the same student has enrolled twice
AND E1.CNAME <> E2.CNAME -- the enrollments are for difference classes
AND E1.CNAME = C1.NAME -- join the class table as C1 for the first enrollment
AND E2.CNAME = C2.NAME -- join the class table as C2 for the second enrollment
AND C1.MEETS_AT = C2.MEETS_AT -- match the start times for the two enrolled classes
只是出于对ANSI SQL等效的兴趣:
select E1.SNUM
from ENROLLED E1
inner join ENROLLED E2 on E1.SNUM = E2.SNUM
inner join CLASS C1 on E1.CNAME = C1.NAME
inner join CLASS C2 on E2.CNAME = C2.NAME
where E1.CNAME <> E2.CNAME
and C1.MEETS_AT = C2.MEETS_AT
和Siyual一样,我更喜欢ANSI版本,但是如果你的大学以旧方式教学,我建议你现在坚持使用它。它的工作原理也很好,很多人仍然使用它。一旦理解了这些概念,就可以选择自己的偏好。