【T-SQL基礎】03.子查詢
閱讀目錄
- 概述:
- 一、獨立子查詢
- 二、相關子查詢
- 三、練習題
以前總是追求新東西,發現基礎才是最重要的,今年主要的目標是精通SQL查詢和SQL性能優化。
本系列【T-SQL基礎】主要是針對T-SQL基礎的總結。
【T-SQL基礎】01.單表查詢-幾道sql查詢題
【T-SQL基礎】02.聯接查詢
【T-SQL基礎】03.子查詢
【T-SQL基礎】04.表表達式-上篇
【T-SQL基礎】04.表表達式-下篇
【T-SQL基礎】05.集合運算
【T-SQL基礎】06.透視、逆透視、分組集
【T-SQL基礎】07.數據修改
【T-SQL基礎】08.事務和並發
【T-SQL基礎】09.可編程對象
----------------------------------------------------------
【T-SQL進階】01.好用的SQL TVP~~獨家贈送[增-刪-改-查]的例子
----------------------------------------------------------
【T-SQL性能調優】01.TempDB的使用和性能問題
【T-SQL性能調優】02.Transaction Log的使用和性能問題
【T-SQL性能調優】03.執行計劃
【T-SQL性能調優】04.死鎖分析
持續更新......歡迎關註我!
練習題:
1.寫一條查詢語句,返回Orders表中活動的最後一天生成的所有訂單。
2.查詢出擁有訂單數量的最多的客戶下過的所有訂單。
3.查詢出2008年5月1號(包括這一天)以後沒有處理過訂單的雇員。
4.查詢2007年下過訂單,而在2008年沒有下過訂單的客戶
5.查詢定購了第12號產品的客戶
回到頂部
概述:
本篇主要是子查詢基礎的總結。
關鍵詞解釋:
外部查詢:查詢結果集返回給調用者
內部查詢:查詢結果集返回給外部查詢。
獨立子查詢:獨立子查詢獨立於其外部查詢的子查詢,可以單獨運行子查詢。在邏輯上,獨立子查詢在執行外部查詢之前先執行一次,接著外部查詢再使用子查詢的結果繼續進行查詢。
相關子查詢:引用了外部查詢中出現的表的子查詢,查詢要依賴於外部查詢,不能獨立地調用它。在邏輯上,子查詢會為每個外部行單獨計算一次。
標量子查詢:返回單個值的子查詢。標量子查詢可以出現在外部查詢中期望使用單個值的任何地方。
多值子查詢:在一個列中
為什麽要使用子查詢?
可以避免在查詢解決方案中把操作分成多個步驟,並在變量中保存中間查詢結果的需要。
回到頂部
一、獨立子查詢
1.獨立標量子查詢(查看練習題1,2)
例子:從HR.Employees表中返回empid最大的員工信息。
可以分兩步:
a.定義一個變量maxid ,通過獨立標量子查詢查詢出empid最大的員工的empid,然後將這個empid保存到變量@maxid中
b.在WHERE條件中過濾出empid = @maxid的記錄
1 2 3 4 5 6 |
DECLARE @maxid AS INT = ( SELECT MAX (empid)
FROM HR.Employees
)
SELECT *
FROM HR.Employees
WHERE empid = @maxid
|
更簡單的方法是嵌套子查詢,只需要一條查詢語句就可以查詢出empid最大的員工信息
1 2 3 4 5 |
SELECT *
FROM HR.Employees
WHERE empid = ( SELECT MAX (empid)
FROM HR.Employees
)
|
註意:
1.對於有效的標量子查詢,它的返回值不能超過一個,如果標量子查詢返回了多個值,在運行時則可能會失敗。
2.如果標量子查詢沒有返回任何值,其結果就轉換為NULL,和NULL行進行比較得到的是UNKNOWN,查詢過濾器不會返回任何讓過濾表達式計算結果為UNKNOWN的行。
2.獨立多值子查詢(查看練習題3)
(1)多值子查詢的語法格式
<標量表達式> IN ( <多值子查詢> )
例子:返回title包含manager的雇員處理過的訂單的信息
方案一:獨立多值子查詢
1 2 3 4 5 |
SELECT *
FROM Sales.Orders
WHERE empid IN ( SELECT empid
FROM HR.Employees
WHERE HR.Employees.title LIKE ‘%Manager‘ )
|
方案二:內聯接查詢
1 2 3 4 |
SELECT *
FROM Sales.Orders
INNER JOIN HR.Employees ON Sales.Orders.empid = HR.Employees.empid
WHERE HR.Employees.title LIKE ‘%Manager‘
|
類似地,很多地方既可以用子查詢也可以用聯接查詢來解決問題。數據庫引擎對兩種查詢的解釋有時候是一樣的,而在另外一些情況下,對二者的解釋則是不同的。可以先用一種查詢解決問題,如果性能不行,再嘗試用聯接替代子查詢,或用子查詢替代聯接。
3.子查詢之distinct關鍵字
當我們想要剔除掉子查詢中的重復值時,會想到在子查詢中不必指定distinct關鍵字,其實是沒有必要的,因為數據庫引擎會幫助我們刪除重復的值,而不用我們顯示指定distinct關鍵字。
回到頂部二、相關子查詢
1.相關子查詢
什麽是相關子查詢:引用了外部查詢中出現的表的列,依賴於外部查詢,不能獨立地運行子查詢。在邏輯上,子查詢會為每個外部行單獨計算一次。
例子:查詢每個客戶返回在他參與活動的最後一天下過的所有訂單。
期望結果:
影響行數:90
1.首先用獨立標量子查詢查詢出最大的訂單日期,返回給外部查詢
1 2 |
SELECT MAX (orderdate)
FROM sales.Orders AS O2
|
2.外部查詢用O1.orderdate進行過濾,過濾出等於最大訂單日期的訂單
3.因為要查詢出每個客戶參與的訂單,所以將獨立標量子查詢改成相關子查詢,用子查詢O2.custid與外查詢O1.custid關聯。
對於O1中每一行,子查詢負責返回當前客戶的最大訂單日期。如果O1中某行的訂單日期和子查詢返回的訂單日期匹配,那麽O1中的這個訂單日期就是當前客戶的最大的訂單日期,在這種情況下,查詢便會返回O1表中的這個行。
1 2 3 |
SELECT MAX (orderdate)
FROM sales.Orders AS O2
WHERE O2.custid = O1.custid
|
綜合上面的步驟,得到下面的查詢語句:
1 2 3 4 5 6 |
SELECT orderid,orderdate,custid
FROM sales.Orders AS O1
WHERE O1.orderdate = ( SELECT MAX (orderdate)
FROM sales.Orders AS O2
WHERE O2.custid = O1.custid
)
|
2.EXISTS謂詞(查看練習題4,5)
- <外查詢> WHERE EXISTS ( 子查詢 )
- 它的輸入是一個子查詢,:如果子查詢能夠返回任何行,改謂詞則返回TRUE,否則返回FALSE.
- 如果子查詢查詢結果又多條,SQL SERVER引擎查詢出一條記錄後,就會立即返回,這種處理方式叫做短路處理。
- Exist謂詞只關心是否存在匹配行,而不考慮SELECT列表中指定的列,所有使用SELECT * FROM TABLE,並沒有什麽負面影響,但是為了展開*代碼的列名會有少少量的開銷,但是還是推薦使用*通配符,查詢語句應該盡可能保持自然和直觀,除非有非常令人信服的理由,才可以犧牲代碼在這方面的要求。
- NOT EXISTS謂詞是EXISTS謂詞的反面
三、練習題
1.寫一條查詢語句,返回Orders表中活動的最後一天生成的所有訂單。
期望結果:
本題考察獨立子查詢的基本用法,首先用獨立子查詢返回最後一天的日期,然後外部查詢過濾出訂單日期等於最後一天的所有訂單。
1 2 3 4 5 6 7 8 9 |
SELECT orderid ,
orderdate ,
custid ,
empid
FROM Sales.Orders
WHERE orderdate = ( SELECT MAX (orderdate)
FROM Sales.Orders
)
|
2.查詢出擁有訂單數量的最多的客戶下過的所有訂單。
期望結果:
本題考察獨立子查詢的用法,和第一題類似,分兩個步驟:
(1)先用子查詢查詢出訂單數量最多的客戶id
(2)然後將id返回給外部查詢,外部查詢通過客戶id過濾出客戶下過的所有訂單
方案一:獨立標量子查詢
1 2 3 4 5 6 7 8 9 10 11 12 |
SELECT custid ,
orderid ,
orderdate ,
empid
FROM Sales.Orders
WHERE custid = ( SELECT TOP ( 1 ) WITH TIES
O.custid
FROM Sales.Orders AS O
GROUP BY custid
ORDER BY COUNT (*) DESC
)
|
註意:
1 | TOP ( 1 ) WITH TIES O.custid |
查找出排序後與第一條記錄O.custid相等的所有行
因為下過訂單數最多的客戶的總訂單數是31,且只有一個客戶(custid=71),所以最後的查詢結果中只有custid=71的客戶下過的所有訂單。
3.查詢出2008年5月1號(包括這一天)以後沒有處理過訂單的雇員。
期望結果:
本題考察獨立子查詢的用法,本題也可以采用兩步來查詢出結果。
(1)首先用子查詢返回所有2008年5月1號(包括這一天)以後處理過訂單的雇員,將這些雇員的empid返回給外部查詢
(2)然後外部查詢用NOT IN過濾出所有2008年5月1號(包括這一天)之後沒有處理過訂單的雇員
方案一:獨立標量子查詢 + NOT IN
1 2 3 4 5 |
SELECT *
FROM HR.Employees
WHERE empid NOT IN ( SELECT empid
FROM Sales.Orders
WHERE orderdate >= ‘20080501‘ )
|
4.查詢2007年下過訂單,而在2008年沒有下過訂單的客戶
期望輸出:
方案一:內聯接+獨立標量子查詢
1.查詢出20070101~20071231所有下過訂單的客戶集合Collection1
1 2 3 |
SELECT DISTINCT C.custid,companyname FROM Sales.Orders O
INNER JOIN Sales.Customers AS C ON C.custid = O.custid
WHERE (orderdate <= ‘20071231‘ AND orderdate >= ‘20070101‘ )
|
2.查詢出20080101~20081231所有下過訂單的客戶結合Collection2
1 2 3 |
SELECT C.custid FROM Sales.Orders O
INNER JOIN Sales.Customers AS C ON C.custid = O.custid
WHERE (orderdate <= ‘20081231‘ AND orderdate >= ‘20080101‘ )
|
3.Collection1不包含Collection2的子集就是2007年下過訂單而在2008年下過訂單的客戶
1 2 3 4 5 6 7 8 9 |
SELECT DISTINCT C.custid,companyname FROM Sales.Orders O
INNER JOIN Sales.Customers AS C ON C.custid = O.custid
WHERE (orderdate <= ‘20071231‘ AND orderdate >= ‘20070101‘ )
AND C.custid NOT IN
(
SELECT C.custid FROM Sales.Orders O
INNER JOIN Sales.Customers AS C ON C.custid = O.custid
WHERE (orderdate <= ‘20081231‘ AND orderdate >= ‘20080101‘ )
)
|
方案二:相關子查詢 EXISTS+NOT EXISTS
1.查詢出20070101~20071231所有下過訂單的客戶集合Collection1
2.查詢出20080101~20081231所有下過訂單的客戶結合Collection2
3.Collection1不包含Collection2的子集就是2007年下過訂單而在2008年下過訂單的客戶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
SELECT C.custid ,
companyname
FROM Sales.Customers AS C
WHERE EXISTS ( SELECT *
FROM Sales.Orders AS O
WHERE O.custid = C.custid
AND ( orderdate <= ‘20071231‘
AND orderdate >= ‘20070101‘
) )
AND NOT EXISTS ( SELECT *
FROM Sales.Orders AS O
WHERE O.custid = C.custid
AND ( orderdate <= ‘20081231‘
AND orderdate >= ‘20080101‘
) )
|
由方案一和方案二,我們可以總結出:INNER JOIN+獨立子查詢可以用Exists+相關子查詢代替
5.查詢訂購了第12號產品的客戶
期望結果:
方案一:內聯接多張表
1 2 3 4 5 6 7 |
SELECT DISTINCT
C.custid ,
companyname
FROM Sales.Customers AS C
INNER JOIN Sales.Orders AS O ON C.custid = O.custid
INNER JOIN Sales.OrderDetails AS D ON O.orderid = D.orderid
WHERE D.productid = ‘12‘
|
方案二:嵌套相關子查詢
1 2 3 4 5 6 7 8 9 10 |
SELECT C.custid ,
companyname
FROM Sales.Customers AS C
WHERE EXISTS ( SELECT *
FROM Sales.Orders AS O
WHERE O.custid = C.custid
AND EXISTS ( SELECT *
FROM Sales.OrderDetails AS D
WHERE D.orderid = O.orderid
AND D.productid = ‘12‘ ) )
|
參考資料:
《SQL2008技術內幕:T-SQL語言基礎》
作 者: Jackson0714
出 處:http://www.cnblogs.com/jackson0714/
關於作者:專註於微軟平臺的項目開發。如有問題或建議,請多多賜教!
版權聲明:本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。
特此聲明:所有評論和私信都會在第一時間回復。也歡迎園子的大大們指正錯誤,共同進步。或者直接私信我
聲援博主:如果您覺得文章對您有幫助,可以點擊文章右下角【推薦】一下。您的鼓勵是作者堅持原創和持續寫作的最大動力!
【T-SQL基礎】03.子查詢