2013-02-10 92 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

相關問題