1. 程式人生 > >翻譯(六)——T-SQL的進階之路:超過基礎的2級水平:寫子查詢

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

相關 完整 圖像 reg 想要 試驗 releases 驗證 不用

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

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

該系列

這篇文章是樓梯系列的一部分:通往T-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語句被稱為外部查詢。子查詢的另一個特性是,它可以獨立於外部查詢運行,並且將運行沒有錯誤,並且可能返回一組行,或者返回一個空行集。

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

以下是使用子查詢時需要考慮的其他事項:

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

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

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

不能在子查詢中使用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/

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