文章目錄
-
- 一、子查詢
- 二、利用子查詢進行過濾
- 三、作為計算字段使用子查詢
- 四、小結
本文介紹什麼是 SQL 子查詢,如何使用它們。子查詢常用于
WHERE
子句的
IN
操作符中,以及用來填充計算列。
一、子查詢
SELECT
語句是 SQL 的查詢。我們迄今為止所看到的所有
SELECT
語句都是簡單查詢,即從單個資料庫表中檢索資料的單條語句。
查詢(query)
任何 SQL 語句都是查詢。但此術語一般指
語句。
SELECT
SQL 還允許建立
子查詢
(subquery),即嵌套在其他查詢中的查詢。為什麼要這樣做呢?了解這個概念的最好方法是考察幾個例子。
二、利用子查詢進行過濾
訂單存儲在兩個表中。每個訂單包含訂單編号、客戶
ID
、訂單日期,在
Orders
表中存儲為一行。各訂單的物品存儲在相關的
OrderItems
表中。
Orders
表不存儲顧客資訊,隻存儲顧客
ID
。顧客的實際資訊存儲在
Customers
表中。
現在,假如需要列出訂購物品
RGAN01
的所有顧客,應該怎樣檢索?下面列出具體的步驟。
(1) 檢索包含物品
RGAN01
的所有訂單的編号。
(2) 檢索具有前一步驟列出的訂單編号的所有顧客的
ID
。
(3) 檢索前一步驟傳回的所有顧客
ID
的顧客資訊。
上述每個步驟都可以單獨作為一個查詢來執行。可以把一條
SELECT
語句傳回的結果用于另一條
SELECT
語句的
WHERE
子句。
也可以使用子查詢來把 3 個查詢組合成一條語句。
第一條
SELECT
語句的含義很明确,它對
prod_id
為
RGAN01
的所有訂單物品,檢索其
order_num
列。輸出列出了兩個包含此物品的訂單:
SELECT order_num
FROM OrderItems
WHERE prod_id = 'RGAN01';
輸出:
order_num
-----------
20007
20008
現在,我們知道了哪個訂單包含要檢索的物品,下一步查詢與訂單
20007
和
20008
相關的顧客
ID
。利用 如何使用 SQL AND、OR、IN 和 NOT 過濾傳回的資料 介紹的
IN
子句,編寫如下的
SELECT
語句:
SELECT cust_id
FROM Orders
WHERE order_num IN (20007,20008);
輸出:
cust_id
----------
1000000004
1000000005
現在,結合這兩個查詢,把第一個查詢(傳回訂單号的那一個)變為子查詢。請看下面的
SELECT
語句:
SELECT cust_id
FROM Orders
WHERE order_num IN (SELECT order_num
FROM OrderItems
WHERE prod_id = 'RGAN01');
輸出:
cust_id
----------
1000000004
1000000005
在
SELECT
語句中,子查詢總是從内向外處理。在處理上面的
SELECT
語句時,DBMS 實際上執行了兩個操作。
首先,它執行下面的查詢:
此查詢傳回兩個訂單号:
20007
和
20008
。然後,這兩個值以
IN
操作符要求的逗号分隔的格式傳遞給外部查詢的
WHERE
子句。外部查詢變成:
可以看到,輸出是正确的,與前面寫死
WHERE
子句所傳回的值相同。
提示:格式化 SQL
包含子查詢的
SELECT
語句難以閱讀和調試,它們在較為複雜時更是如此。如上所示,把子查詢分解為多行并進行适當的縮進,能極大地簡化子查詢的使用。
順便一提,這就是顔色編碼起作用的地方,好的 DBMS 用戶端正是出于這個原因使用了顔色代碼 SQL。
現在得到了訂購物品
RGAN01
的所有顧客的
ID
。下一步是檢索這些顧客
ID
的顧客資訊。檢索兩列的 SQL 語句為:
SELECT cust_name, cust_contact
FROM Customers
WHERE cust_id IN (1000000004,1000000005);
可以把其中的
WHERE
子句轉換為子查詢,而不是寫死這些顧客
ID
:
SELECT cust_name, cust_contact
FROM Customers
WHERE cust_id IN (SELECT cust_id
FROM Orders
WHERE order_num IN (SELECT order_num
FROM OrderItems
WHERE prod_id = 'RGAN01'));
輸出:
cust_name cust_contact
----------------------------- --------------------
Fun4All Denise L. Stephens
The Toy Store Kim Howard
為了執行上述
SELECT
語句,DBMS 實際上必須執行三條
SELECT
語句。最裡邊的子查詢傳回訂單号清單,此清單用于其外面的子查詢的
WHERE
子句。
外面的子查詢傳回顧客
ID
清單,此顧客
ID
清單用于最外層查詢的
WHERE
子句。最外層查詢傳回所需的資料。
可見,在
WHERE
子句中使用子查詢能夠編寫出功能很強且很靈活的 SQL 語句。對于能嵌套的子查詢的數目沒有限制,不過在實際使用時由于性能的限制,不能嵌套太多的子查詢。
注意:隻能是單列
作為子查詢的
語句隻能查詢單個列。企圖檢索多個列将傳回錯誤。
SELECT
注意:子查詢和性能
這裡給出的代碼有效,并且獲得了所需的結果。但是,使用子查詢并不總是執行這類資料檢索的最有效方法。更多的論述,請參閱 如何使用 SQL INNER JOIN 聯結兩個或多個表,其中将再次給出這個例子。
三、作為計算字段使用子查詢
使用子查詢的另一方法是建立計算字段。假如需要顯示
Customers
表中每個顧客的訂單總數。訂單與相應的顧客
ID
存儲在
Orders
表中。
執行這個操作,要遵循下面的步驟:
(1) 從
Customers
表中檢索顧客清單;
(2) 對于檢索出的每個顧客,統計其在
Orders
表中的訂單數目。
正如前兩課所述,可以使用
SELECT COUNT(*)
對表中的行進行計數,并且通過提供一條
WHERE
子句來過濾某個特定的顧客
ID
,僅對該顧客的訂單進行計數。
例如,下面的代碼對顧客
1000000001
的訂單進行計數:
SELECT COUNT(*) AS orders
FROM Orders
WHERE cust_id = 1000000001;
要對每個顧客執行
COUNT(*)
,應該将它作為一個子查詢。請看下面的代碼:
SELECT cust_name,
cust_state,
(SELECT COUNT(*)
FROM Orders
WHERE Orders.cust_id = Customers.cust_id) AS orders
FROM Customers
ORDER BY cust_name;
輸出:
cust_name cust_state orders
------------------------- ---------- ------
Fun4All IN 1
Fun4All AZ 1
Kids Place OH 0
The Toy Store IL 1
Village Toys MI 2
這條
SELECT
語句對
Customers
表中每個顧客傳回三列:
cust_name
、
cust_state
和
orders
。
orders
是一個計算字段,它是由圓括号中的子查詢建立的。該子查詢對檢索出的每個顧客執行一次。在此例中,該子查詢執行了 5 次,因為檢索出了 5 個顧客。
子查詢中的
WHERE
子句與前面使用的
WHERE
子句稍有不同,因為它使用了完全限定列名,而不隻是列名(
cust_id
)。它指定表名和列名(
Orders.cust_id
和
Customers.cust_id
)。
下面的
WHERE
子句告訴 SQL,比較
Orders
表中的
cust_id
和目前正從
Customers
表中檢索的
cust_id
:
WHERE Orders.cust_id = Customers.cust_id
用一個句點分隔表名和列名,在有可能混淆列名時必須使用這種文法。在這個例子中,有兩個
cust_id
列:一個在
Customers
中,另一個在
Orders
中。如果不采用完全限定列名,DBMS 會認為要對
Orders
表中的
cust_id
自身進行比較。因為
SELECT COUNT(*) FROM Orders WHERE cust_id = cust_id
總是傳回
Orders
表中訂單的總數,而這個結果不是我們想要的:
SELECT cust_name,
cust_state,
(SELECT COUNT(*)
FROM Orders
WHERE cust_id = cust_id) AS orders
FROM Customers
ORDER BY cust_name;
輸出:
cust_name cust_state orders
------------------------- ---------- ------
Fun4All IN 5
Fun4All AZ 5
Kids Place OH 5
The Toy Store IL 5
Village Toys MI 5
雖然子查詢在構造這種
SELECT
語句時極有用,但必須注意限制有歧義的列。
注意:完全限定列名
你已經看到了為什麼要使用完全限定列名,沒有具體指定就會傳回錯誤結果,因為 DBMS 會誤解你的意思。
有時候,由于出現沖突列名而導緻的歧義性,會引起 DBMS 抛出錯誤資訊。
例如,
或
WHERE
ORDER BY
子句指定的某個列名可能會出現在多個表中。
好的做法是,如果在
語句中操作多個表,就應使用完全限定列名來避免歧義。
SELECT
提示:不止一種解決方案
正如本文前面所述,雖然這裡給出的樣例代碼運作良好,但它并不是解決這種資料檢索的最有效方法。在 如何使用 SQL INNER JOIN 聯結兩個或多個表 和 SQL 如何使用自聯結、自然聯結和外聯結 學習 JOIN 時,我們還會遇到這個例子。
四、小結
本文介紹了什麼是子查詢,如何使用它們。子查詢常用于
WHERE
子句的
IN
操作符中,以及用來填充計算列。我們舉了這兩種操作類型的例子。
原文連結:https://www.developerastrid.com/sql/sql-subqueries/
(完)