SQL查询:如何更改表会导致不同的结果?

时间:2015-05-27 20:36:51

标签: mysql sql sqlite

数据库是:

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中的所有值。

我真的认为改变朋友的位置不会对结果产生影响,但结果似乎不同。

这种现象令人困惑,如果有专家能提供一些帮助,我真的很感激。

3 个答案:

答案 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