数据库是:
Highschooler(身份证,姓名,年级)英语:有一名高中学生,具有唯一身份证和特定年级的名字。
朋友(ID1,ID2)英语:ID1的学生是ID2学生的朋友。友谊是相互的,所以如果(123,456)在朋友表中,那么(456,123)。
喜欢(ID1,ID2)英语:ID1的学生喜欢ID2的学生。喜欢某人不一定是相互的,所以如果(123,456)在Likes表中,则不能保证(456,123)也存在。
任务是:找到只有同一年级朋友的学生的姓名和成绩。返回按年级排序的结果,然后按每个年级的名称返回。
这两个命令似乎应该产生相同的结果,但事实并非如此。
正确的命令是:
select distinct name, grade
from Highschooler s1
where not exists (select * from Highschooler s2, Friend
where Friend.ID2 = s2.ID
and s1.ID = Friend.ID1
and s2.grade <> s1.grade)
order by grade, name
但我也认为以下只改变朋友位置的命令也有意义:
select distinct name, grade
from Highschooler s1, Friend
where not exists (select * from Highschooler s2
where Friend.ID2 = s2.ID
and s1.ID = Friend.ID1
and s2.grade <> s1.grade)
order by grade, name
但后者只返回表Highschooler中的所有值。
我真的认为改变朋友的位置不会对结果产生影响,但结果似乎不同。
这种现象令人困惑,如果有专家能提供一些帮助,我真的很感激。
答案 0 :(得分:1)
首先,您应该避免使用隐式逗号分隔的连接(例如from Highschooler s2, Friend
)并使用显式连接(from Highschooler s2 inner join Friend on ...
)。它们更具可读性,更不容易出错。
您的第一个查询会查找高中生,其中不存在具有其他成绩的朋友高中生。 DISTINCT似乎是多余的。好吧,你会删除碰巧与另一个人有相同名称和等级的人。
你的第二个查询交叉连接高中生和朋友表,即得到所有组合是否相关。对于每个这样的组合,它确保不存在高学龄儿童,其中组合的朋友恰好是高中生的朋友并且朋友具有另一个年级。因此,您保留了高中学生和不属于一起的朋友的所有组合。简而言之:这个查询毫无意义。
实际上,交叉连接表很少有意义,并且您并不想交叉加入它们。你错误地这样做了,因为你使用了旧的以逗号分隔的连接语法,该语法在二十多年前被显式连接所取代。如果您已经告诉DBMS要加入表以便获取相关记录(即from Highschooler s2 inner join Friend
),它会告诉您缺少ON子句来指定记录的相关性。
答案 1 :(得分:0)
原因是当您将friend表放在exists之外时,您将使用where子句从exists语句中排除部分记录。
如果删除where子句,则会显示缺失的记录。
〜CiscoKid
答案 2 :(得分:0)
您的第一个查询也会返回没有朋友的人,您应该添加...
and exists (select * from Highschooler s2, Friend
where Friend.ID2 = s2.ID
and s1.ID = Friend.ID1
and s2.grade = s1.grade)
...到你的where
条款。
我已经测试了您的第二个查询here并且它返回了每条记录。您的Highschooler s1与子查询外的Friend表之间没有任何关系。如果您将Friends表放入子查询中,如下所示:
select id, name, grade
from Highschooler s1
where not exists (select id from Highschooler s2, Friend
where Friend.ID2 = s2.ID
and s1.ID = Friend.ID1
and s2.grade <> s1.grade)
...并添加......
and exists (select id from Highschooler s2, Friend
where Friend.ID2 = s2.ID
and s1.ID = Friend.ID1
and s2.grade = s1.grade)
...它将返回所需的记录。经过测试here。