1. 程式人生 > >【T-SQL基礎】03.子查詢

【T-SQL基礎】03.子查詢

引用 鏈接 編程 pac tom 單表 獨立 但是 company

閱讀目錄

  • 概述:
  • 一、獨立子查詢
  • 二、相關子查詢
  • 三、練習題

以前總是追求新東西,發現基礎才是最重要的,今年主要的目標是精通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.查詢出200851號(包括這一天)以後沒有處理過訂單的雇員。

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謂詞(查看練習題45)

  1. <外查詢> WHERE EXISTS ( 子查詢 )
  2. 它的輸入是一個子查詢,:如果子查詢能夠返回任何行,改謂詞則返回TRUE,否則返回FALSE.
  3. 如果子查詢查詢結果又多條,SQL SERVER引擎查詢出一條記錄後,就會立即返回,這種處理方式叫做短路處理。
  4. Exist謂詞只關心是否存在匹配行,而不考慮SELECT列表中指定的列,所有使用SELECT * FROM TABLE,並沒有什麽負面影響,但是為了展開*代碼的列名會有少少量的開銷,但是還是推薦使用*通配符,查詢語句應該盡可能保持自然和直觀,除非有非常令人信服的理由,才可以犧牲代碼在這方面的要求。
  5. 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.查詢出200851號(包括這一天)以後沒有處理過訂單的雇員。

期望結果:

技術分享

本題考察獨立子查詢的用法,本題也可以采用兩步來查詢出結果。

1)首先用子查詢返回所有200851號(包括這一天)以後處理過訂單的雇員,將這些雇員的empid返回給外部查詢

2)然後外部查詢用NOT IN過濾出所有200851號(包括這一天)之後沒有處理過訂單的雇員

方案一:獨立標量子查詢 + 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.子查詢