1. 程式人生 > 其它 >sql join 子查詢_跟飛哥學程式設計:SQL入門-10:子查詢

sql join 子查詢_跟飛哥學程式設計:SQL入門-10:子查詢

技術標籤:sql join 子查詢sql 子查詢sql 找到最近的值sql子查詢sql遞迴查詢所有子節點成績的查詢和排序功能實現

be42ae9e3e7dd8de1ff73a03b617741f.png

我們來看這麼一個需求:

查詢每個學生的成績和所有同學平均成績的差距。

直觀的想,實現這個需求,至少需要兩個SELECT:

  • 一個SELECT獲取所有同學的平均成績
  • 另一個SELECT獲取每個同學和平均成績之間的差距及其他資訊

所以,我們就需要使用一個叫做 子查詢 的技術,其SQL程式碼如下所示:

SELECT Id, Reward, 
Reward-(SELECT AVG(Reward) FROM TProblem) AS Gap 
FROM TProblem

檢視執行計劃,可以發現確實進行了兩次SELECT操作:

3985e0fab0da34ea5545c30b92175a62.png

子查詢除了可以使用在SELECT子句中,也可以使用在其他地方。比如我們來看另外一個需求:

用id=7的學生的成績替換掉id=8的學生成績

我們當然要使用UPDATE,然後使用子查詢獲得UPDATE應使用的值,其SQL語句如下所示:

UPDATE Student 
SET Score = (SELECT Score FROM Student WHERE Id = 7) 
WHERE Id = 8

我們可以看出,這種子查詢就是將SELECT的查詢結果當做一個列來使用,在任何可以使用“列名”的地方,都可以使用子查詢。

子查詢的結果可是一個標量,也可以是一個集合。

比如,我們看這麼一個需求:

刪除Student表中成績的重複記錄

如何來判讀重複?很難!所以我們就得“逆向思維”,能不能找到“唯一”的那些記錄特徵,然後將其和整表進行對比?所以就是DELETE和SELECT的組合操作:

DELETE Student 
WHERE Id NOT IN (                                -- 使用NOT IN進行集合操作
SELECT MAX(Id) FROM Student GROUP BY Score)      -- 其結果是一個集合

注意:通過Score進行分組,但不要以Score為結果值進行比較。使用Max(Id)獲取Id,Id才能唯一標識一行資料。

上述子查詢都被歸類為獨立子查詢,其特點是子查詢是獨立的,不依賴於子查詢SQL語句以外的變數;換言之,你完全可以獨立的執行子查詢。

還有點懵?沒關係,我們接著看例子:

繼續使用之前的考試成績(Exam)表,

查詢並統計出每個學生分數最高的考試成績資訊(不僅僅是最高分數)

如果僅僅是每個學生的最高分數,用MAX(Score)...GROUP BY [UserName]就OK了,但是我們還要獲取其他資訊,怎麼辦呢?用我們剛剛學到的子查詢,這樣寫,有沒有問題?

SELECT * FROM Exam
WHERE Score IN(SELECT MAX(Score) FROM Exam GROUP BY [UserName])

結果是不行的。比如王新,他的最高成績應該是91,但結果裡還包括了他89的一行:

f25db753ad6f0791bb95f4488d77930e.png
左邊是原始資料,右邊是查詢結果

為什麼呢?因為子查詢中Max(Score)了獲取了所有人的最好成績。89是陳元的最好成績,卻不是王新的最好成績啊!上述SQL語句,就類似於:

SELECT * FROM Exam WHERE Score IN(89,93,95,...)

結果就是隻要成績是89的所有行都滿足條件了。

所以得在子查詢中“定位到人”,正確的寫法:

SELECT * FROM Exam oe                  -- 給外部表一個別名oe(必須)
WHERE Score = (
    SELECT MAX(Score) FROM Exam ie     -- 給內部表一個別名ie(必須)
    WHERE oe.UserName = ie.UserName    -- 在內部查詢中使用了外部的UserName
    -- 不再需要GROUP
)

注意其語法要點:

  1. 內表外表都必須加別名,以便於區分
  2. 然後使用表別名引導列名

這樣查詢的結果就對了:

b0b0ff4fdee60bd45c95e1dee708c579.png

從邏輯上我們可以這樣理解:

  1. 在外表從上到下依次遍歷,
  2. 每選中一行,就把這一行的UserName傳入子查詢中
  3. 子查詢使用上一步獲得的外表UserName進行MAX()運算,獲得外表指定的UserName的最好成績,並傳遞給外表
  4. 外部利用子查詢獲得的(最好)成績和本行Score進行比較:結果為真,保留;否則過濾

但是,這只是我們“邏輯上地”“想象中地”,而不是實際上SQL Server的執行過程。檢視執行計劃:

b4f989f0f22ef5d5cbe714df5db89587.png

事實上SQL Server是利用了Sort(排序)、Segment(分組)和TOP(擷取)就完成了上述需求。其中的原理,鑑於課程安排,我們就不做介紹了。但同學們通過這個例子,應該可以深刻的體會:

SQL語言是一種指令性性的、目標導向的語言

了吧?

這種在子查詢內部中使用外部變數的查詢,就被稱之為相關子查詢。(請對比獨立子查詢理解)

為了鞏固學習內容,我們再來實現一個需求:

查出每個學生的其成績高於等於該學生平均成績的Exam資訊
SELECT * FROM Exam oe
WHERE Score >= (SELECT AVG(Score) FROM Exam ie WHERE oe.Author = ie.Author)

作業

使用子查詢:

  1. 找到從未成為邀請人的使用者(當心NULL值)
  2. 查出這些文章:其作者總共只發布過這一篇文章
  3. 為求助新增一個釋出時間(PublishTime),查詢每個作者最近釋出的一篇求助
  4. 查出每個作者懸賞最多的3篇求助
  5. 刪除懸賞相同的求助(只要相同的全部刪除一個不留)
  6. 刪除每個作者懸賞最低的求助

----------------------------

暫時不做:如果一篇求助的關鍵字大於3個,將它的懸賞值翻倍


感謝童鞋們的閱讀!^_^

我就是:黑律師/包工頭/創業狗/老碼農……現在還是教書匠的大飛哥。

再次重申這個系列的目標是:

1)通俗易懂。2)實戰為主。3)面向就業。

系列內容的完善需要你的反饋!

歡迎點贊和評論,以及加入我們的QQ交流群:326801052。