其實有些新的特性在SQL Server早就已經出現過,可是若非系統的去學習數據庫你會發如今實際項目中別人的SQL實際上是比較複雜的,其實利用新的SQL Server語法會更加方便和簡潔,從本節開始咱們將講述一些SQL Server中早已出現的新語法,簡短的內容,深刻的理解,Always to reivew the basics。數據庫
APPLY運算符是一個很是強大的表運算符,可是APPLY不是標準的,相對應的標準叫作LATERAL,可是此標準並未在SQL Server中實現。像全部表運算符同樣,該運算符用於查詢的FROM子句中。APPLY運算符支持的類型是CROSS APPLY和OUTER APPLY。CROSS APPY僅僅實施一個邏輯查詢處理階段,而OUTER APPLY實施了兩個階段,APPLY運算符對兩個輸入表進行操做,第二個能夠是一個表表達式,咱們將APPLY兩側的表分別叫作左側表和右側表,右側表一般是一個派生表或TVF(內嵌表值函數)。CROSS APPLY運算符實施一個邏輯查詢處理階段-它將右側的表表達式應用到左側表的每一行,並生成一個組合結果集的結果表。CROSS APPLYl相似於交叉聯接中的CROSS JOIN,可是使用CROSS APPLY運算符,右側的表表達式能夠對來自左側表的每一行表示一個不一樣的行集,這是與聯接的不一樣之處。當在右側使用一個派生表,而且派生表查詢中引用來自左側表的屬性,就能夠實現此目標,或者是在右側使用一個內嵌TVF,能夠傳遞左側的屬性做爲輸入參數,一樣能夠實現此目的-摘抄自SQL Server 2012基礎教程。下面咱們看一個簡單的例子。函數
USE TSQL2012 GO SELECT C.custid, A.orderid, A.orderdate FROM Sales.Customers AS C CROSS APPLY (SELECT TOP(3) orderid, empid, orderdate, requireddate FROM Sales.Orders AS O WHERE O.custid = C.custid ORDER BY orderdate DESC, orderid DESC) AS A;
上述完成的是返回每一個客戶最近的3個訂單。咱們能夠將右側的表表達式看作是一個相關子查詢,右側的表表達式經過引用custid對來自Customers表的每一行進行處理並返回每一個客戶的最近的3個訂單,是否是看起來很清爽呢,下面咱們將進一步探討APPLY運算符的做用。性能
上面咱們看到經過相關子查詢來進行查詢顯得代碼有點醜陋,咱們再來看一個例子。查詢每一個單價最高的訂單,咱們經過子查詢來實現。學習
USE AdventureWorks2012 GO SELECT SalesOrderID ,OrderDate ,MaxUnitPrice =(SELECT MAX(sod.UnitPrice) FROM Sales.SalesOrderDetail sod WHERE soh.SalesOrderID = sod.SalesOrderID) FROM Sales.SalesOrderHeader AS soh
如上操做看似代碼比較簡潔也能完成咱們的查詢訴求,可是咱們用派生表來進行查詢又是怎樣的呢? ui
USE AdventureWorks2012 GO SELECT soh.SalesOrderID ,soh.OrderDate ,sod.max_unit_price FROM Sales.SalesOrderHeader AS soh JOIN ( SELECT max_unit_price = MAX(sod.UnitPrice), SalesOrderID FROM Sales.SalesOrderDetail AS sod GROUP BY sod.SalesOrderID ) sod ON sod.SalesOrderID = soh.SalesOrderID
此時因爲兩個表徹底不相關,咱們須要經過GROUP BY完成再進行JOIN,代碼不是顯得很是臃腫嗎,這仍是簡單的,當有多個表時就比較複雜了,致使代碼就再也不具備可讀性。可是自從在SQL Server 2005中有了APPLY媽媽不再用擔憂我讀不懂複雜的代碼了,咱們看看CROSS APPLY是怎樣實現的。spa
USE AdventureWorks2012 GO SELECT soh.SalesOrderID ,soh.OrderDate ,sod.max_unit_price FROM Sales.SalesOrderHeader AS soh CROSS APPLY ( SELECT max_unit_price = MAX(sod.UnitPrice) FROM Sales.SalesOrderDetail AS sod WHERE soh.SalesOrderID = sod.SalesOrderID ) sod
當咱們利用內部聯接時此時JOIN中的查詢是獨立的因此須要進行GROUP BY,而對於CROSS APPLY它自己就是對來自左側的表中每一行就行處理並返回,同時利用CROSS APPLY它也超越了相關子查詢,好比說咱們還須要查出每一個訂單的總價呢,咱們利用相關子查詢須要再次嵌入SELECT子句。code
SELECT SalesOrderID ,OrderDate ,MaxUnitPrice = (SELECT MAX(sod.UnitPrice) FROM Sales.SalesOrderDetail sod WHERE soh.SalesOrderID = sod.SalesOrderID) ,SumLineTotal = (SELECT SUM(LineTotal) FROM Sales.SalesOrderDetail sod WHERE soh.SalesOrderID = sod.SalesOrderID) FROM Sales.SalesOrderHeader AS soh
而利用CROSS APPLY只需添加集合函數SUM便可blog
USE AdventureWorks2012 GO SELECT soh.SalesOrderID ,soh.OrderDate ,sod.max_unit_price ,sod.sum_line_total FROM Sales.SalesOrderHeader AS soh CROSS APPLY ( SELECT max_unit_price = MAX(sod.UnitPrice) ,sum_line_total = SUM(sod.LineTotal) FROM Sales.SalesOrderDetail AS sod WHERE soh.SalesOrderID = sod.SalesOrderID ) sod
對於OUTER APPLY,若是右側的表表達式返回一個空集合,CROSS APPLY運算符不會返回相應的左側行,也就是說OUTER APPLY和在派生表上進行LEFT JOIN是等同的,以下:教程
SELECT soh.SalesOrderID ,soh.OrderDate ,sod.max_unit_price FROM Sales.SalesOrderHeader AS soh LEFT JOIN ( SELECT max_unit_price = MAX(sod.UnitPrice), SalesOrderID FROM Sales.SalesOrderDetail AS sod GROUP BY sod.SalesOrderID ) sod ON sod.SalesOrderID = soh.SalesOrderID
此時咱們利用OUTER APPLY則是以下:it
USE AdventureWorks2012 GO SELECT soh.SalesOrderID ,soh.OrderDate ,sod.max_unit_price FROM Sales.SalesOrderHeader AS soh OUTER APPLY ( SELECT max_unit_price = MAX(sod.UnitPrice) FROM Sales.SalesOrderDetail AS sod WHERE soh.SalesOrderID = sod.SalesOrderID ) sod
上述對於APPLY右側表表達式是一個派生表,此時爲了封裝,咱們可使用TVF內嵌表值函數來實現。其實將內嵌表值函數來代替派生表實現每一個客戶最近的3個訂單。首先咱們封裝一個表值函數
USE TSQL2012 GO IF OBJECT_ID('dbo.TopOrders') IS NOT NULL DROP FUNCTION dbo.TopOrders; GO CREATE FUNCTION dbo.TopOrders (@custid AS INT, @n AS INT) RETURNS TABLE AS RETURN SELECT orderid, empid, orderdate, requireddate FROM Sales.Orders WHERE custid = @custid ORDER BY orderdate DESC, orderid DESC OFFSET 0 ROWS FETCH FIRST @n ROWS ONLY; GO
接着利用CROSS APPLY進行查詢。
USE TSQL2012 GO SELECT C.custid, C.companyname, A.orderid, A.empid, A.requireddate FROM Sales.Customers AS C CROSS APPLY dbo.TopOrders(C.custid, 3) AS A;
上面咱們經過封裝內嵌表值函數代替派生表使代碼更具可讀性和可維護性。到此咱們能夠得出一點基本結論。
APPLY運算符使用分析結論:當須要對錶中的每一行進行應用時,且須要將全部結果集組合到一個結果集表中時,此時咱們應該使用APPLY運算符,至因而使用CROSS APPLY仍是OUTER APPLY根據場景而定,雖然APPLY右側表能夠用相關子查詢或者派生表來實現,可是使得代碼臃腫和可維護性差,經過封裝內嵌表值函數來實現能夠說是對右側表經過相關子查詢或者派生表來實現的完美替代者。
本節咱們講解了APPLY運算符中兩種類型的使用,下一節咱們來分析下關於CROSS APPLY VS INNER JOIN的性能問題,同時也說明下CROSS APPLY和OUTER APPLY的應用場景。簡短的內容,深刻的理解,咱們下節再會。