SQL 關聯子查詢


學習重點

  • 關聯子查詢會在細分的組內進行比較時使用。

  • 關聯子查詢和 GROUP BY 子句一樣,也可以對表中的數據進行切分。

  • 關聯子查詢的結合條件如果未出現在子查詢之中就會發生錯誤。

一、普通的子查詢和關聯子查詢的區別

按此前所學,使用子查詢就能選取出銷售單價(sale_price)高於全部商品平均銷售單價的商品。這次我們稍稍改變一下條件,選取出各商品種類中高於該商品種類的平均銷售單價的商品。

  • 按照商品種類與平均銷售單價進行比較

    只通過語言描述可能難以理解,還是讓我們來看看具體示例吧。我們以廚房用具中的商品為例,該分組中包含了表 1 所示的 4 種商品。

    表 1 廚房用具中的商品

    商品名稱 銷售單價
    菜刀 3000
    高壓鍋 6800
    叉子 500
    擦菜板 880

    因此,計算上述 4 種商品的平均價格的算術式如下所示。

    (3000 + 6800 + 500 + 880) / 4 = 2795 (日元)

    這樣我們就能得知該分組內高於平均價格的商品是菜刀和高壓鍋了,這兩種商品就是我們要選取的對象。

    我們可以對余下的分組繼續使用同樣的方法。衣服分組的平均銷售單價是:

    (1000 + 4000) / 2 = 2500 (日元)

    因此運動T恤就是要選取的對象。辦公用品分組的平均銷售單價是:

    (500 + 100) / 2 = 300 (日元)

    因此打孔器就是我們要選取的對象。

    這樣大家就能明白該進行什么樣的操作了吧。我們並不是要以全部商品為基礎,而是要以細分的組為基礎,對組內商品的平均價格和各商品的銷售單價進行比較。

    按照商品種類計算平均價格並不是什么難事,我們已經學習過了,只需按照代碼清單 15 那樣,使用 GROUP BY 子句就可以了。

    代碼清單 15 按照商品種類計算平均價格

    SELECT AVG(sale_price)
    FROM Product
    GROUP BY product_type;
    

    但是,如果我們使用前一節(標量子查詢)的方法,直接把上述 SELECT 語句使用到 WHERE 子句當中的話,就會發生錯誤。

    -- 發生錯誤的子查詢
    SELECT product_id, product_name, sale_price
    FROM Product
    WHERE sale_price > (SELECT AVG(sale_price)
                            FROM Product
                            GROUP BY product_type);
    

    出錯原因前一節已經講過了,該子查詢會返回 3 行結果(2795、2500、300),並不是標量子查詢。在 WHERE 子句中使用子查詢時,該子查詢的結果必須是單一的。

    但是,如果以商品種類分組為單位,對銷售單價和平均單價進行比較,除此之外似乎也沒有其他什么辦法了。到底應該怎么辦才好呢?

  • 使用關聯子查詢的解決方案

    這時就輪到我們的好幫手——關聯子查詢登場了。

    KEYWORD

    • 關聯子查詢

    只需要在剛才的 SELECT 語句中追加一行,就能得到我們想要的結果了 [1]。事實勝於雄辯,還是讓我們先來看看修改之后的 SELECT 語句吧(代碼清單 16)。

    代碼清單 16 通過關聯子查詢按照商品種類對平均銷售單價進行比較

    通過關聯子查詢按照商品種類對平均銷售單價進行比較

    特定的 SQL

    Oracle 中不能使用 AS(會發生錯誤)。因此,在 Oracle 中執行代碼清單 16 時,請大家把 ① 中的 FROM Product AS P1 變為 FROM Product P1,把 ② 中的 FROM Product AS P2 變為 FROM Product P2

    執行結果

    product_type  | product_name  | sale_price
    ---------------+---------------+------------
    辦公用品      | 打孔器         |        500
    衣服          | 運動T恤        |       4000
    廚房用具      | 菜刀           |       3000
    廚房用具      | 高壓鍋         |       6800
    

    這樣我們就能選取出辦公用品、衣服和廚房用具三類商品中高於該類商品的平均銷售單價的商品了。

    這里起到關鍵作用的就是在子查詢中添加的 WHERE 子句的條件。該條件的意思就是,在同一商品種類中對各商品的銷售單價和平均單價進行比較。

    這次由於作為比較對象的都是同一張 Product 表,因此為了進行區別,分別使用了 P1P2 兩個別名。在使用關聯子查詢時,需要在表所對應的列名之前加上表的別名,以“<表名>.<列名>”的形式記述。

    在對表中某一部分記錄的集合進行比較時,就可以使用關聯子查詢。因此,使用關聯子查詢時,通常會使用“限定(綁定)”或者“限制”這樣的語言,例如本次示例就是限定“商品種類”對平均單價進行比較。

    法則 8

    在細分的組內進行比較時,需要使用關聯子查詢。

二、關聯子查詢也是用來對集合進行切分的

換個角度來看,其實關聯子查詢也和 GROUP BY 子句一樣,可以對集合進行切分。

大家還記得我們用來說明 GROUP BY 子句的圖(圖 6)嗎?

根據商品種類對表進行切分的圖示

圖 6 根據商品種類對表進行切分的圖示

上圖顯示了作為記錄集合的表是如何按照商品種類被切分的。使用關聯子查詢進行切分的圖示也基本相同(圖 7)。

根據關聯子查詢進行切分的圖示

圖 7 根據關聯子查詢進行切分的圖示

我們首先需要計算各個商品種類中商品的平均銷售單價,由於該單價會用來和商品表中的各條記錄進行比較,因此關聯子查詢實際只能返回 1 行結果。這也是關聯子查詢不出錯的關鍵。關聯子查詢執行時,DBMS 內部的執行情況如圖 8 所示。

關聯子查詢執行時 DBMS 內部的執行情況

圖 8 關聯子查詢執行時 DBMS 內部的執行情況

如果商品種類發生了變化,那么用來進行比較的平均單價也會發生變化,這樣就可以將各種商品的銷售單價和平均單價進行比較了。關聯子查詢的內部執行結果對於初學者來說是比較難以理解的,但是像上圖這樣將其內部執行情況可視化之后,理解起來就變得非常容易了吧。

三、結合條件一定要寫在子查詢中

下面給大家介紹一下 SQL 初學者在使用關聯子查詢時經常犯的一個錯誤,那就是將關聯條件寫在子查詢之外的外層查詢之中。請大家看一下下面這條 SELECT 語句。

錯誤的關聯子查詢書寫方法

上述 SELECT 語句只是將子查詢中的關聯條件移到了外層查詢之中,其他並沒有任何更改。但是,該 SELECT 語句會發生錯誤,不能正確執行。允許存在這樣的書寫方法可能並不奇怪,但是 SQL 的規則禁止這樣的書寫方法。

該書寫方法究竟違反了什么規則呢?那就是關聯名稱作用域。雖然這一術語看起來有些晦澀難懂,但是一解釋大家就明白了。關聯名稱就是像 P1P2 這樣作為表別名的名稱,作用域(scope)就是生存范圍(有效范圍)。也就是說,關聯名稱存在一個有效范圍的限制。

KEYWORD

  • 關聯名稱

  • 作用域

具體來講,子查詢內部設定的關聯名稱,只能在該子查詢內部使用(圖 9)。換句話說,就是“內部可以看到外部,而外部看不到內部”。

請大家一定不要忘記關聯名稱具有一定的有效范圍。如前所述,SQL 是按照先內層子查詢后外層查詢的順序來執行的。這樣,子查詢執行結束時只會留下執行結果,作為抽出源的 P2 表其實已經不存在了 [2]。因此,在執行外層查詢時,由於 P2 表已經不存在了,因此就會返回“不存在使用該名稱的表”這樣的錯誤。

子查詢內的關聯名稱的有效范圍

圖 9 子查詢內的關聯名稱的有效范圍

請參閱

(完)


  1. 事實上,對於代碼清單 16 中的 SELECT 語句,即使在子查詢中不使用 GROUP BY 子句,也能得到正確的結果。這是因為在 WHERE 子句中追加了“P1.product_type=P2.product_type”這個條件,使得 AVG 函數按照商品種類進行了平均值計算。但是為了跟前面出錯的查詢進行對比,這里還是加上了 GROUP BY 子句。 ↩︎

  2. 當然,消失的其實只是 P2 這個名稱而已,Product 表以及其中的數據還是存在的。 ↩︎


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM