翻譯(六)——T-SQL的進階之路:超過基礎的2級水平:寫子查詢

T-SQL的進階之路:超過基礎的2級水平:寫子查詢

格雷戈裏·拉森(Gregory Larsen),2016/01/01(第一次出版:2014/01/29)

該系列

這篇文章是樓梯系列的一部分:通往T-SQL的樓梯:超越基礎sql

從他的階梯到T - SQL DML, Gregory Larsen涵蓋了T-SQL語言的更高級的方面,如子查詢。數據庫

當你在開始建立比基本Transact-SQL語句更復雜的SQL代碼時,你可能會發現須要使用其餘SELECT語句的結果來約束您的查詢。當您在父Transact-SQL語句中嵌入SELECT語句時,這些嵌入的SELECT語句被稱爲子查詢或相關子查詢。在這個層次的基礎上,我將討論子查詢的不一樣方面,在之後的級別中,我將討論相關的子查詢。函數

什麼是子查詢?

一個子查詢就是一個包含其餘Transact-SQL語句的查詢語句,一個子查詢能夠被用在任何表達式,許多子查詢結果返回單個結果由於他們被用在鏈接比較操做(=,!=,<=,>,>=)或者表達。當子查詢不用做表達式或與比較運算符使用時,它能夠返回多個值。此外,子查詢甚至能夠返回多個列和值,當它們在FROM子句或關鍵字中使用時。工具

在Transact-SQL語句中很容易發現子查詢,由於它將是括號中包含的SELECT語句。因爲子查詢包含在Transact-SQL語句中,子查詢一般被稱爲內部查詢。而包含子查詢的Transact-SQL語句被稱爲外部查詢。子查詢的另外一個特性是,它能夠獨立於外部查詢運行,而且將運行沒有錯誤,而且可能返回一組行,或者返回一個空行集。sqlserver

子查詢的另外一種形式是相關子查詢。可是關聯子查詢不能獨立於外部Transact SQL語句運行。關聯子查詢使用來自外部查詢的列或列來約束從相關子查詢返回的結果。對於本文的相關子查詢,這已經足夠了。我將在將來的樓梯文章中探索相關的子查詢。性能

如下是使用子查詢時須要考慮的其餘事項:測試

ntext、文本和圖像數據類型不容許從子查詢返回spa

除非使用頂級運算符,不然不能在子查詢中使用ORDER BY子句code

不能更新使用子查詢的視圖server

不能在子查詢中使用COMPUTE和INTO子句

子查詢示例的示例數據

  爲了演示如何使用子查詢,我須要一些測試數據。個人全部示例都將使用AdventureWorks2008R2數據庫,而不是建立本身的測試數據。若是你想跟隨並運行在您的環境中個人例子你能夠從這裏(http://msftdbprodsamples.codeplex.com/releases/view/93587 )下載AdventureWorks2008R2數據庫。

返回單個值的子查詢示例

如上所述,在比較操做符的一側使用表達式或返回值的子查詢須要返回一個值。Transact SQL語句中有許多不一樣的地方須要子查詢返回一個列值,就像在一個選擇列表,where子句等。在本節中,我將提供一系列的例子將演示使用子查詢表達式或比較運算符,以知足不一樣的業務需求。

子查詢列列表中

  列表中的子查詢是一個SELECT語句,它返回SELECT子句的列列表中的單個列值。爲了演示如何在選擇列表中使用子查詢,咱們假設必須從SELECT語句中生成一個具備如下業務需求的結果集:

返回全部的銷售數據。SalesOrderHeader記錄了一個訂單日期等於「2007 -02-19 00:00. 00」

按SalesOrderID順序訂購返回的記錄

每一行返回最古老的順序的行數爲1的行數,下一個最大的行數爲2,等等

結果集須要一個名爲TotalOrders的列,該列須要包含有一個與「2007 -02-19 00:00. 00」相同的OrderDate的訂單總數。

知足這些需求的代碼如清單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的語句中嵌入的SELECT語句,它周圍有括號。我已經提取了子查詢語句並將其放入清單2中,以防您想要測試驗證它是否能夠獨立於完整的Transact -SQL語句運行。

SELECT COUNT(*) FROM [Sales].[SalesOrderHeader] WHERE OrderDate = '2007-02-19 00:00:00.000'

清單2:在清單1中找到的子查詢語句

經過在列列表中擁有這個子查詢,Listing1中的transact - sql語句可以計算有一個OrderDate「2007 -02-19 00:00. 000」的SalesOrderHeader行的數量,並返回該信息以及關於銷售的詳細行信息。有相同的OrderDate值的SalesOrderHeader記錄。

子查詢在WHERE子句中的例子

 

有時您但願根據SELECT語句的結果來驅動WHERE子句條件。當您在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中的子查詢位於WHERE條件的右側。這個子查詢標識生產的產品。產品名稱的產品名稱爲「長袖標誌球衣,XL」。這個子查詢容許我找到全部的銷售。SalesOrderDetail記錄有一個與「長袖Logo Jersey,XL」的產品名稱相關的產品。

示例使用子查詢來控制TOP子句

使用TOP子句返回的行數能夠由表達式控制。清單5中的代碼標識了銷售數量。SalesOrderDetail行應該基於頂部子句中的子查詢返回。

SELECT TOP (SELECT TOP 1 OrderQty FROM [Sales].[SalesOrderDetail] ORDER BY ModifiedDate) * FROM [Sales].[SalesOrderDetail] WHERE ProductID = 716;

清單4:TOP子句中的子查詢

 

清單4中的代碼使用從子查詢返回的OrderQty值來標識將在TOP子句中使用的值。經過使用子查詢來控制TOP子句返回的行數,能夠建立一個子查詢,該子查詢將動態識別在運行時從查詢返回的行數。

 

以子查詢在HAVING子句中的例子舉例

爲了演示在「有」子句中使用子查詢,假設您有如下業務需求:

生成包含sales . salesorderheader的結果集。訂單日期和每一個日期的訂單數量,其中訂單數量超過了「2006 -05- 01」的訂單數量。

爲了知足這一要求,我已經開發了清單6中的查詢,其中使用了「HAVING子句」中的子查詢。

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:HAVING子句中的子查詢

清單5中的代碼在有子句的右邊有子查詢,並在個人子查詢中使用COUNT函數來肯定在「2006-05-01」上放置的訂單數量。

在函數調用中使用子查詢的例子

爲了演示在函數調用中使用子查詢,假設您有要求在OrderDate和每一個Sales的最大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中的代碼有兩個不一樣的子查詢。兩個子查詢都返回Sales中的max OrderDate。SalesOrderHeader表。可是,第一個子查詢用於將日期傳遞給DATEDIFF函數的第二個參數。

返回多個值的子查詢示例

到目前爲止,個人全部示例都包含子查詢,這些子查詢僅在單個列中返回單個值。並非全部的子查詢都有這樣的要求。接下來的幾個示例將使用返回多個值和/或多個列的子查詢。

子查詢在FROM子句中的示例

在FROM子句中,您一般肯定您的transact - sql語句將針對的表或表集合。每一個表提供一組記錄,您的查詢將使用這些記錄來肯定您的查詢的最終結果集。子查詢能夠看做是返回一組記錄的查詢,所以它能夠像表同樣在FROM子句中使用。清單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子句中的子查詢建立一個表別名,名爲last10 salesorders。個人子查詢返回了最後10個銷售。alesOrderDetail記錄包含一個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錶鏈接起來。經過這樣作,我能夠肯定一個不一樣的訂單日期,這是最後10次人們訂購的產品= 716。

使用IN關鍵字進行子查詢的示例

另外一個能夠編寫子查詢的地方,該子查詢返回一個列的多個值,當子查詢生成一個用於關鍵字的記錄集時。清單9中的代碼演示瞭如何使用子查詢將值傳遞到in關鍵字。

SELECT * FROM [Sales].[SalesOrderDetail] WHERE ProductID IN (SELECT ProductID FROM [Production].[Product] WHERE Name like '%XL%');

清單9:使用子查詢將值傳遞到IN關鍵字

 

清單9中的代碼使用子查詢返回產品的不一樣值。包含字符「XL」的產品表。這些從子查詢返回的ProductID值而後在in關鍵字中使用,以約束從銷售中返回的行。SalesOrderDetail表。

 

在修改數據的語句中使用子查詢的例子

 

到目前爲止,個人全部示例都演示瞭如何在SELECT語句的不一樣部分中使用子查詢。子查詢也能夠在插入、更新或刪除語句中使用。清單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:插入語句中的子查詢

 

在清單10的代碼中,我使用子查詢來計算插入到列MaxOrderDate中的值。這僅僅是如何在INSERT語句中使用子查詢的一個示例。請記住,子查詢也能夠在UPDATE和/或DELETE語句中使用。

 

子查詢與鏈接之間的性能考慮

 

若是你讀過的「子查詢基本面」文檔由微軟(http://technet.microsoft.com/en-us/library/ms189575(v = sql.105). aspx),那麼您可能已經運行在這個聲明中關於性能包含子查詢的語句:

「在transact - sql中,包含子查詢和語義等價的語句之間一般沒有性能差別。」

爲了比較使用子查詢和不使用子查詢的查詢的性能,我將重寫清單3中的子查詢以使用鏈接操做。清單11顯示了我從新編寫的JOIN查詢,它至關於清單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中使用子查詢的查詢的性能和使用JOIN的清單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.ProductID WHERE P.Name = 'Long-Sleeve Logo Jersey, XL';

清單12:測試清單3和清單4的性能的代碼

在運行清單12中的代碼後,我查看了由「SET STATISTICS」語句生成的消息。經過查看統計數據,我發現兩個查詢對SalesOrderDetail表有3,309個邏輯讀取,2個邏輯讀針對產品表,每一個都使用31毫秒的CPU。另外,我回顧了爲這兩個查詢建立的SQL Server的執行計劃。我發現SQL Server對這兩種狀況都產生了相同的執行計劃。所以,在個人狀況下使用子查詢或鏈接查詢產生等價的性能,就像微軟所記錄的那樣。

 

摘要

 

子查詢是嵌入另外一個transact - sql語句的SELECT語句。子查詢能夠獨立於外部查詢運行,所以有時被稱爲獨立查詢。請記住,每當有一個子查詢替表明達式時,或者它與比較運算符一塊兒使用,它只能返回單個列和值。一般可使用JOIN邏輯重寫子查詢。子查詢是幫助您構建更復雜的transact - sql語句以知足業務需求的強大工具。

 

原文連接:http://www.sqlservercentral.com/articles/Stairway+Series/104517/

相關文章
相關標籤/搜索