是否有更优雅的方式编写此SQL查询?

时间:2013-02-10 16:07:31

标签: sql sqlite

我正在为Stanfords介绍数据库课程,这是家庭作业之一。我的代码做得很好,但我真的不喜欢它如何重复使用相同的SELECT-FROM-JOIN部分两次:

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子句重用它两次=和&lt;&gt;?

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

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

4 个答案:

答案 0 :(得分: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的{​​{1}}返回一行或多行,则true条件为SELECT;否则,它是false。您需要做的就是将内部子查询关联与外部子查询(F.ID1=ME.ID部分)相关联,并添加所需的其余约束(OTHER.Grade = ME.GRADE或{ {1}})查询。

答案 1 :(得分: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值)。

编辑:

这个版本回答了这个问题:哪些高中生有朋友,他们都属于同一年级。你的问题很模糊。也许你的意思是朋友原来的人都在同一年级。如果是这样,那么你可以通过一个小的修改来做到这一点。一种方法是将having子句更改为:

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

这会检查朋友原始人是否都在同一年级。

答案 2 :(得分: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引擎使用关键字“MINUS”,有些使用“EXCEPT”。

但请注意,与UNION非常相似,这将执行两个查询,然后过滤其结果。这可以具有与单个全部查询不同的性能,但请注意不一定更糟。很多时候我发现它甚至有更好的性能,因为“超出”单列,特别是排序,非常快

另外,如果您的数据库引擎允许,您可能会尝试使用View或CTE来缩短原始查询,但除了美学外,我没有太大意义这样做

答案 3 :(得分:0)

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

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

其他数据库支持相同的概念,但使用关键字除外,而不是减去。