2013-02-10 88 views
3

我正在做数据库课程的斯坦福德简介,这是家庭作业之一。我的代码做这项工作很好,但我真的不喜欢它,我如何重复使用相同的SELECT-FROM-JOIN部分两次:是否有写这个SQL查询更优雅的方式?

SELECT name, grade 
FROM Highschooler 
WHERE 
    ID IN (
     SELECT H1.ID 
     FROM Friend 
     JOIN Highschooler AS H1 
      ON Friend.ID1 = H1.ID 
     JOIN Highschooler AS H2 
      ON Friend.ID2 = H2.ID 
     WHERE H1.grade = H2.grade  
    ) AND 
    ID NOT IN (
     SELECT H1.ID 
     FROM Friend 
     JOIN Highschooler AS H1 
      ON Friend.ID1 = H1.ID 
     JOIN Highschooler AS H2 
      ON Friend.ID2 = H2.ID 
     WHERE H1.grade <> H2.grade 
    ) 
ORDER BY grade, name 

这是在代码中使用的两个表的SQL架构:

Highschooler(ID int, name text, grade int); 
Friend(ID1 int, ID2 int); 

我不得不查询所有只有朋友在同一年级而不是其他年级的高中生。有没有办法以某种方式只写一次下面的代码,并重复使用两次两个不同的WHERE子句=和<>?

SELECT H1.ID 
    FROM Friend 
    JOIN Highschooler AS H1 
     ON Friend.ID1 = H1.ID 
    JOIN Highschooler AS H2 
     ON Friend.ID2 = H2.ID 

编辑:我们需要提供SQLite代码。

回答

0

有些数据库支持minus关键字。

select whatever 
from wherever 
where id in 
(select id 
from somewhere 
where something 
minus 
select id 
from somewhere 
where something else 
) 

其他数据库支持相同的概念,但使用关键字except,而不是minus。

4

这是WHERE EXISTS查询“海报孩子”的例子:

SELECT name, grade 
FROM Highschooler ME 
WHERE EXISTS (
    SELECT 1 
    FROM Friend F 
    JOIN Highschooler OTHER on F.ID2=OTHER.ID 
    WHERE F.ID1=ME.ID AND OTHER.Grade = ME.GRADE 
) 
AND NOT EXISTS (
    SELECT 1 
    FROM Friend F 
    JOIN Highschooler OTHER on F.ID2=OTHER.ID 
    WHERE F.ID1=ME.ID AND OTHER.Grade <> ME.GRADE 
) 

EXISTS条件true如果SELECT返回一个或多个行;否则,它是false。您只需要将关联内部子查询与外部子查询(F.ID1=ME.ID部分),并将您需要的其余约束(OTHER.Grade = ME.GRADEOTHER.Grade <> ME.GRADE)添加到查询中。

1

当您将某些过滤连接变为集合操作(​​如UNION或MINUS/EXCEPT)时,有时可以获得更自然的查询形状。你的查询可以是例如写为(伪代码):

SELECT H.id 
    FROM Highschooler H 
    JOIN .... | has a friend 
    WHERE ... | in SAME grade 

EXCEPT 

    SELECT H.id 
    FROM Highschooler H 
    JOIN .... | has a friend 
    WHERE ... | in OTHER grade 

一些SQL引擎使用关键字“减”,有的用“除”。

但请注意,非常喜欢UNION,这将执行两个查询,然后筛选其结果。这可以有不同的表现,然后一个单一的所有查询,但要注意的是不一定更糟。很多时候,我发现它甚至具有更好的性能,因为除了单列以外,特别是排序的“除外”非常快,而且,如果您的数据库引擎允许,您可以尝试使用View或CTE来缩短原始查询,但我没有看到这么做,除了美观外

4

这是关于与个人相关的群体的典型问题类型。当你面对这样一个问题时,一种方法就是使用连接(成对看待事物)。通常更好的方法是使用聚合来一次查看整个组。

这里的洞察是,如果你有一群朋友,并且都在同一年级,那么最小和最大成绩将是相同的。

该提示可能足以让您编写查询。如果是这样,请到这里。

返回所需内容的查询比您所做的要简单得多。你只需要看看朋友们的成绩:

SELECT f.id1 
FROM Friend f jJOIN 
    Highschooler fh 
    ON Friend.ID1 = fh.ID join 
group by f.id1 
having max(fh.grade) = min(fh.grade) 

having子句确保所有都是一样的(忽略NULL值)。

编辑:

这个版本回答的问题是:哪些highschoolers有朋友的所有的人都在同一档次。你的问题不明确。也许你的意思是说朋友原来的人都在同一年级。如果是这样,那么你可以做一个小的修改。一种方法是将having条款改为:

having max(fh.grade) = min(fh.grade) and 
     max(fh.grade) = (select grade from Highschooler h where f.id1 = h.id1) 

这就验证了朋友原来的人都在同一档次。

+0

+1 yep,这可能是最好的建议,因为它正确地利用了我们对于要返回的数据的所有信息,并且它在查询的同一层执行此操作。 – quetzalcoatl 2013-02-10 16:20:12

+0

我很抱歉,但我不明白我如何使用/熟练掌握您的代码。我尝试了它的各种变化,并没有一个返回正确的记录。 – pootzko 2013-02-10 17:14:35

相关问题