通往t - sql的階梯:超越基本級別2:編寫子查詢sql
格雷戈裏·拉森(Gregory Larsen),2016/01/01(第一次出版:2014/01/29)數據庫
該系列函數
這篇文章是樓梯的一部分系列:t - sql的階梯:除了基礎知識工具
從他的t - sql DML樓梯後,格雷戈裏·拉森涵蓋了更高級的子查詢等方面的t - sql語言。性能
在某些時候當你開始建立更復雜的SQL代碼,超越基本transact - SQL語句,您可能會發現須要限制你的查詢使用其餘SELECT語句的結果。測試
當你在父母transact - sql語句嵌入一個SELECT語句,這些嵌入的SELECT語句被稱爲子查詢,或相關子查詢。優化
在本層的樓梯的基本內容外,我將討論子查詢的不一樣方面,在將來我將討論相關子查詢。code
子查詢是什麼?開發
子查詢是一個SELECT語句是包含在數據庫引擎執行另外一個sql語句。文檔
可使用子查詢可使用任何一個表達式。
許多子查詢返回一列值,由於他們結合使用比較運算符(=,!
=、<、< =、>、> =),或者一個表達式。
當不使用子查詢表達式或比較運算符能夠返回多個值。
此外,子查詢能夠返回多個列和價值觀在FROM子句中使用或與關鍵字的存在。
數據庫引擎執行子查詢是容易被髮如今一個sql語句,由於它將SELECT語句包含在圓括號中。
因爲數據庫引擎執行子查詢包含在一個sql語句查詢一般被稱爲內部查詢。
而transact - sql語句包含子查詢被稱爲外部查詢。
子查詢的另外一個特色是它能夠獨立運行的外部查詢和運行沒有錯誤,並可能返回的一組行,或一個空行。
另外一種形式的子查詢是相關子查詢。
但相關子查詢不能獨立運行的外部交易的SQL語句。
相關子查詢使用列或列從外部查詢限制相關子查詢返回的結果。
這是對這篇文章的相關子查詢。
我將探索相關子查詢在之後的樓梯。
這裏有一些其餘的事情時要考慮使用子查詢:
ntext、文本和圖像數據類型不容許從子查詢返回
ORDER BY子句不能用於使用子查詢,除非頂級運營商
視圖使用子查詢不能被更新
計算和條款不能使用子查詢
樣本數據的子查詢的例子
爲了演示如何使用子查詢我須要一些測試數據。
而不是建立我本身的測試數據,我全部的示例將使用AdventureWorks2008R2數據庫。
若是你想跟隨並運行在您的環境中個人例子你能夠從這裏下載AdventureWorks2008R2數據庫:http://msftdbprodsamples.codeplex.com/releases/view/93587
返回一個值的子查詢的例子
如上所述,子查詢中使用一個表達式或返回一個值比較運算符的一側必須返回一個值。
transact - sql語句中有許多不一樣的地方須要子查詢返回一個列值,就像在一個選擇列表,where子句,等等。在本節中,我將提供一系列的例子將演示使用子查詢表達式或比較運算符,以知足不一樣的業務需求。
子查詢列列表中
子查詢的列列表是一個SELECT語句返回一列值的列列表放置在一個SELECT子句。
爲了演示如何使用選擇列表的子查詢假設咱們必須從一個SELECT語句產生一個結果集,如下業務需求:
返回全部的銷售。
OrderDate SalesOrderHeader記錄有什麼等於「2007-02-19 00:00:00.000」
SalesOrderID順序返回的記錄
返回每一行數最古老秩序RowNumber 1,下一個古老的RowNumber 2,等等
須要一個結果集列名叫TotalOrders須要填充的總訂單數量有OrderDate等於「2007-02-19 00:00:00.000」
知足這些需求的代碼如清單1所示。
SELECT ROW_NUMBER() OVER (ORDER BY SalesOrderID) RowNumber , (SELECT COUNT(*) FROM [Sales].[SalesOrderHeader] WHERE ModifiedDate = '2007-02-19 00:00:00.000') AS TotalOrders , * FROM [Sales].[SalesOrderHeader] WHERE OrderDate = '2007-02-19 00:00:00.000';
清單1:子查詢的列列表
在這個單一transact - sql聲明中看到兩個不一樣的選擇條款。
子查詢的SELECT語句嵌入在清單1,括號中的語句。
我拿出了子查詢語句和把它在清單2中,若是您想測試來驗證它能夠獨立完成transact - sql語句的運行。
SELECT COUNT(*) FROM [Sales].[SalesOrderHeader] WHERE OrderDate = '2007-02-19 00:00:00.000'
清單2:子查詢語句中找到清單1所示
經過這個子查詢的列列表,這Listing1 transact - sql語句能夠數一數的OrderDate SalesOrderHeader行有一個「2007-02-19 00:00:00.000」並返回信息以及有關銷售的詳細行信息。
OrderDate SalesOrderHeader記錄有相同的價值。
子查詢的WHERE子句的例子
有時你想開車WHERE子句條件基於一個SELECT語句的結果。
當你一個SELECT語句的WHERE子句這個SELECT語句是一個真正的子查詢。
爲了演示在WHERE子句中使用子查詢,假設你須要顯示銷售。
SalesOrderDetail記錄包含購買超大長袖商標運動衫。
清單3中的代碼使用子查詢知足個人顯示要求。
SELECT * FROM [Sales].[SalesOrderDetail] WHERE ProductID = (SELECT ProductID FROM [Production].[Product] WHERE Name = 'Long-Sleeve Logo Jersey, XL');
清單3:子查詢的WHERE子句
清單3中的查詢是在右邊的條件。
這個子查詢標識一個ProductID生產。
產品記錄的名稱是「長袖標誌的球衣,XL。
這個子查詢容許我找到全部的銷售。
SalesOrderDetail記錄有ProductID與產品名稱相關聯的「長袖標誌的球衣,XL」。
示例使用子查詢控制條款
返回的行數使用上面的條款能夠控制的一個表達式。
清單5中的代碼標識的數量銷售。
SalesOrderDetail行應該返回基於子查詢在條款。
SELECT TOP (SELECT TOP 1 OrderQty FROM [Sales].[SalesOrderDetail] ORDER BY ModifiedDate) * FROM [Sales].[SalesOrderDetail] WHERE ProductID = 716;
清單4:子查詢最佳條款
清單4中的代碼使用OrderQty子查詢返回的值來標識的值將用於條款。
經過使用子查詢控制返回的行數,上面條款,容許您動態構建子查詢,肯定在運行時從查詢返回的行數。
子查詢的例子有條款
爲了演示使用子查詢子句,假設你有如下業務需求:
產生一個結果集,其中包含Sales.SalesOrderHeader。
OrderDate和每一個日期,訂單的數量,訂單的數量超過了訂單的數量了‘2006-05-01’。
爲了達到這個要求我開發了清單6中的查詢中使用子查詢子句。
SELECT count(*), OrderDate FROM [Sales].[SalesOrderHeader] GROUP BY OrderDate HAVING count(*) > (SELECT count(*) FROM [Sales].[SalesOrderHeader] WHERE OrderDate = '2006-05-01 00:00:00.000');
清單5:子查詢的條款
清單5中的代碼的子查詢右邊有條款和使用計數函數在子查詢來肯定訂單的數量在「2006-05-01」。
在函數調用中使用子查詢的例子
爲了演示在函數調用中使用子查詢,假設您有要求顯示OrderDate之間的天數和最大OrderDate爲每一個銷售。
SalesOrderHeader記錄。
清單6中的代碼知足這個需求。
SELECT SalesOrderID , OrderDate ,DATEDIFF ( dd,OrderDate ,(SELECT MAX(OrderDate) FROM [Sales].[SalesOrderHeader]) ) AS DaysBetweenOrders ,(SELECT MAX(OrderDate) FROM [Sales].[SalesOrderHeader]) AS MaxOrderDate FROM [Sales].[SalesOrderHeader];
清單6:在函數調用子查詢
清單6中的代碼有兩個不一樣的子查詢。
兩個子查詢返回的馬克斯向銷售。
SalesOrderHeader表。
但第一子查詢是用來傳遞一個日期DATEDIFF函數的第二個參數。
子查詢返回多個值的例子
到目前爲止,我全部的例子包含子查詢只返回一個值在一個列中。
並非全部的子查詢需求。
接下來的幾個例子將使用子查詢返回多個值和/或多個列。
子查詢的FROM子句的例子
在FROM子句一般肯定一個表或一組表,你transact - sql語句將對運做。
每一個表提供了一組記錄您的查詢將使用來肯定最終的查詢結果集。
子查詢能夠被認爲是一個查詢,返回一組記錄,所以它能夠用於從條款就像一張桌子。
查詢我在清單7顯示瞭如何使用子查詢的FROM子句。
子查詢時FROM子句中使用子查詢的結果集生產一般被稱爲一個派生表。
SELECT SalesOrderID FROM (SELECT TOP 10 SalesOrderID FROM [Sales].[SalesOrderDetail] WHERE ProductID = 716 ORDER BY ModifiedDate DESC) AS Last10SalesOrders
清單7:子查詢的FROM子句
清單7中的代碼使用子查詢的FROM子句建立一個表別名,Last10SalesOrders命名。
個人子查詢返回過去10銷售。
alesOrderDetail記錄包含ProductID 716。
個人代碼在清單7中是一個很是簡單的例子,如何使用子查詢的FROM子句。
經過使用子查詢的FROM子句你能夠從語法構造更復雜的鏈接與其餘表子查詢的結果,或額外的子查詢,就像我在清單8中。
SELECT DISTINCT OrderDate FROM (SELECT TOP 10 SalesOrderID FROM [Sales].[SalesOrderDetail] WHERE ProductID = 716 ORDER BY ModifiedDate DESC) AS Last10SalesOrders JOIN [Sales].[SalesOrderHeader] AS SalesOrderHeader ON Last10SalesOrders.SalesOrderID = SalesOrderHeader.SalesOrderID ORDER BY OrderDate
清單8:加入一個派生表與一個真實的表
在清單8中我建立的子查詢/派生表我在清單7中,加入SalesOrderHeader表。
這樣我能夠肯定OrderDate大相徑庭的最後一人命令ProductID = 716的10倍。
使用子查詢的關鍵字的例子
另外一個地方,您能夠編寫一個查詢,它返回多個值的列是當你的子查詢生成一個記錄集,使用的關鍵字。
清單9中的代碼演示瞭如何經過使用子查詢的關鍵字值。
SELECT * FROM [Sales].[SalesOrderDetail] WHERE ProductID IN (SELECT ProductID FROM [Production].[Product] WHERE Name like '%XL%');
清單9:傳遞值的關鍵字使用子查詢
清單9中的代碼使用子查詢來從生產爲ProductID返回不一樣的值。
產品表,有一個名稱包含字符「XL」。
這些ProductID值返回的子查詢中使用在關鍵字來限制返回的行銷售。
SalesOrderDetail表。
在一份聲明中使用子查詢的例子,修改數據
目前爲止我全部的示例演示如何使用子查詢在一個SELECT語句的不一樣部分。
子查詢也可被用來在一個INSERT、UPDATE或DELETE語句。
清單10中的代碼展現瞭如何使用子查詢在一個INSERT語句。
DECLARE @SQTable TABLE ( OrderID int, OrderDate datetime, TotalDue money, MaxOrderDate datetime); -- INSERT with SubQuery INSERT INTO @SQTable SELECT SalesOrderID, OrderDate, TotalDue, (SELECT MAX(OrderDate) FROM [Sales].[SalesOrderHeader]) FROM [Sales].[SalesOrderHeader] WHERE CustomerID = 29614; -- Display Records SELECT * FROM @SQtable;
清單10:子查詢在一個INSERT語句
在個人代碼在清單10中,我使用子查詢計算值列MaxOrderDate插入。
這只是一個例子,如何在INSERT語句中使用子查詢。
記住子查詢也可被用來在一個更新和/或DELETE語句。
子查詢之間的性能考慮,加入
若是你讀過的「子查詢基本面」文檔由微軟(http://technet.microsoft.com/en-us/library/ms189575(v = sql.105). aspx),那麼您可能已經運行在這個聲明中關於性能包含子查詢的語句:
「在transact - sql,一般沒有語句之間的性能差別,包括子查詢和語義上等價版本不。」
比較查詢的性能使用子查詢和一個等價查詢不使用子查詢我重寫個人子查詢在清單3中使用鏈接操做。
清單11顯示了查詢重寫鏈接查詢,至關於我在清單3中。
SELECT SOD.* FROM [Sales].[SalesOrderDetail] AS SOD INNER JOIN [Production].[Product] AS P ON SOD.ProductID = P.ProductID WHERE P.Name = 'Long-Sleeve Logo Jersey, XL';
清單11:加入查詢等價於清單3中的查詢
比較查詢的性能在清單3中使用子查詢和查詢在清單11中,使用加入我將使用清單12中的代碼運行兩個查詢。
SET STATISTICS IO ON; SET STATISTICS TIME ON; -- Listing 3 query SELECT * FROM [Sales].[SalesOrderDetail] WHERE ProductID = (SELECT ProductID FROM Production.Product WHERE Name = 'Long-Sleeve Logo Jersey, XL'); -- Listing 11 query SELECT SOD.* FROM [Sales].[SalesOrderDetail] AS SOD INNER JOIN [Production].[Product] AS P ON SOD.ProductID = P.ProductIDo
WHERE P.Name = 'Long-Sleeve Logo Jersey, XL';
清單12:代碼清單3和清單4的測試性能
清單12中的代碼運行後,我回顧了消息產生的「統計」報表。
經過回顧兩種查詢統計我發現有3309對SalesOrderDetail表邏輯讀,和對產品表2邏輯讀,每31女士的CPU使用。
另外我回顧了執行計劃,SQL Server建立這兩個查詢。
我發現SQL Server產生相同的執行計劃。
所以使用子查詢或加入查詢個人處境產生等效性能,一樣記錄了微軟。
總結
子查詢是一個SELECT語句嵌入式數據庫引擎執行與另外一個sql語句。
外查詢的子查詢能夠獨立運行,所以有時被稱爲一個獨立的查詢。
記住,任什麼時候候你有一個查詢的一個表達式,或是使用比較運算符,它能夠只返回單個列和價值。
一般子查詢可使用加入重寫邏輯。
子查詢是一個強大的工具來幫助你創建你的數據庫引擎執行更復雜的sql語句來知足您的業務需求。
問題和答案
在本節中,您能夠檢查你理解如何使用子查詢概念,回答下列問題。
問題1:
完成這個句子「子查詢是一個數據庫引擎執行SELECT語句在另外一個sql語句,_____________________。
不是不能獨立運行完整的查詢。
引用列從外部查詢。
當獨立運行的外部查詢將返回的結果。
問題2:
何時子查詢只須要返回一個列和值(選擇全部適用)?
子查詢時在FROM子句中使用
子查詢時使用的條款
在子查詢中使用一個表達式
子查詢時使用比較運算符
問題3:
transact - sql語句的WHERE子句中使用子查詢老是比等效的查詢執行速度較慢,不包含子查詢(真或假)?
真正的
假
答案:
問題1:
正確答案是c。外查詢的子查詢能夠獨立運行,它將返回結果。
它不須要任何列從外部查詢,若是有列外查詢稱爲相關子查詢。
問題2:
正確答案是c和d。子查詢時須要返回一列值用做表達式,或者比較操做。
子查詢時使用的關鍵字能夠返回一個或多個值的列。
若是子查詢在FROM子句中使用它能夠返回一個列和一個值,但它也能夠返回多個列和價值觀。
問題3:
正確的答案是錯誤的。
SQL Server優化器很聰明和極可能計算相同的兩個等價的查詢執行計劃。
若是一個查詢的執行計劃,其中包含子查詢和一個等價的無子查詢都獲得相同的執行計劃而後查詢都有相同的性能。