1. 程式人生 > 其它 >Cross Apply的用法

Cross Apply的用法

GPS平臺、網站建設、軟體開發、系統運維,找森大網路科技!
https://cnsendnet.taobao.com
來自森大科技官方部落格
http://www.cnsendblog.com/index.php/?p=2029

透過執行計劃可以看出,cross apply類似不帶where條件的連線即cross join 。形式上會靈活些.
使用 APPLY 運算子可以為實現查詢操作的外部表表達式返回的每個行呼叫表值函式。表值函式作為右輸入,外部表表達式作為左輸入
。通過對右輸入求值來獲得左輸入每一行的計算結果,生成的行被組合起來作為最終輸出。APPLY 運算子生成的列的列表是左輸入中
的列集,後跟右輸入返回的列的列表。
APPLY 有兩種形式: CROSS APPLY 和 OUTER APPLY。CROSS APPLY 僅返回外部表中通過表值函式生成結果集的行。OUTER APPLY 既返
回生成結果集的行,也返回不生成結果集的行,其中表值函式生成的列中的值為 NULL。

SQL Server 2005 新增 cross apply 和 outer apply 聯接語句,增加這兩個東東有啥作用呢?

我們知道有個 SQL Server 2000 中有個 cross join 是用於交叉聯接的。實際上增加 cross apply 和 outer apply 是用於交叉聯接表值函式(返回表結果集的函式)的, 更重要的是這個函式的引數是另一個表中的欄位。這個解釋可能有些含混不請,請看下面的例子:

-- 1. cross join 聯接兩個表

select *

from TABLE_1 as T1

cross join TABLE_2 as T2

-- 2. cross join 聯接表和表值函式,表值函式的引數是個“常量”

select *

from TABLE_1 T1

cross join FN_TableValue(100)

-- 3. cross join 聯接表和表值函式,表值函式的引數是“表T1中的欄位”

select *

from TABLE_1 T1

cross join FN_TableValue(T1.column_a)

Msg 4104, Level 16, State 1, Line 1

The multi-part identifier "T1.column_a" could not be bound.

最後的這個查詢的語法有錯誤。在 cross join 時,表值函式的引數不能是表 T1 的欄位, 為啥不能這樣做呢?我猜可能微軟當時沒有加這個功能:),後來有客戶抱怨後, 於是微軟就增加了 cross apply 和 outer apply 來完善,請看 cross apply, outer apply 的例子:

-- 4. cross apply

select *

from TABLE_1 T1

cross apply FN_TableValue(T1.column_a)

-- 5. outer apply

select *

from TABLE_1 T1

outer apply FN_TableValue(T1.column_a)

cross apply 和 outer apply 對於 T1 中的每一行都和派生表(表值函式根據T1當前行資料生成的動態結果集) 做了一個交叉聯接。cross apply 和 outer apply 的區別在於: 如果根據 T1 的某行資料生成的派生表為空,cross apply 後的結果集 就不包含 T1 中的這行資料,而 outer apply 仍會包含這行資料,並且派生表的所有欄位值都為 NULL。

下面的例子摘自微軟 SQL Server 2005 聯機幫助,它很清楚的展現了 cross apply 和 outer apply 的不同之處:

-- cross apply

select *

from Departments as D

cross apply fn_getsubtree(D.deptmgrid) as ST

deptid deptname deptmgrid empid empname mgrid lvl

----------- ----------- ----------- ----------- ----------- ----------- ------

1 HR 2 2 Andrew 1 0

1 HR 2 5 Steven 2 1

1 HR 2 6 Michael 2 1

2 Marketing 7 7 Robert 3 0

2 Marketing 7 11 David 7 1

2 Marketing 7 12 Ron 7 1

2 Marketing 7 13 Dan 7 1

2 Marketing 7 14 James 11 2

3 Finance 8 8 Laura 3 0

4 R&D 9 9 Ann 3 0

5 Training 4 4 Margaret 1 0

5 Training 4 10 Ina 4 1

(12 row(s) affected)

-- outer apply

select *

from Departments as D

outer apply fn_getsubtree(D.deptmgrid) as ST

deptid deptname deptmgrid empid empname mgrid lvl

----------- ----------- ----------- ----------- ----------- ----------- ------

1 HR 2 2 Andrew 1 0

1 HR 2 5 Steven 2 1

1 HR 2 6 Michael 2 1

2 Marketing 7 7 Robert 3 0

2 Marketing 7 11 David 7 1

2 Marketing 7 12 Ron 7 1

2 Marketing 7 13 Dan 7 1

2 Marketing 7 14 James 11 2

3 Finance 8 8 Laura 3 0

4 R&D 9 9 Ann 3 0

5 Training 4 4 Margaret 1 0

5 Training 4 10 Ina 4 1

6 Gardening NULL NULL NULL NULL NULL

(13 row(s) affected)

注意 outer apply 結果集中多出的最後一行。 當 Departments 的最後一行在進行交叉聯接時:deptmgrid 為 NULL,fn_getsubtree(D.deptmgrid) 生成的派生表中沒有資料,但 outer apply 仍會包含這一行資料,這就是它和 cross join 的不同之處。

    1. 小例子一

cross apply的經典用法;求每個人考試科目中成績的前兩名(取出每組中的前幾名)

  1. 疑問

USE tempdb

GO

-- 用於分拆字串的表值函式

CREATE FUNCTION dbo.f_Split(

@str varchar(max)

)RETURNS @re TABLE(

id int IDENTITY, val varchar(10))

AS

BEGIN

DECLARE @pos int

SET @pos = CHARINDEX(',', @str)

WHILE @pos > 0

BEGIN

INSERT @re(val) VALUES(LEFT(@str, @pos - 1))

SELECT

@str = STUFF(@str, 1, @pos, ''),

@pos = CHARINDEX(',', @str)

END

IF @str > ''

INSERT @re(val) VALUES(@str)

RETURN

END

GO

-- 用於分拆的示例資料

DECLARE @tb TABLE(

col varchar(max))

INSERT @tb

SELECT col = '1,2,3' UNION ALL

SELECT col = NULL UNION ALL

SELECT col = '' UNION ALL

SELECT col = '1'

-- 使用 CROSS APPLY

SELECT *

FROM @tb A

CROSS APPLY dbo.f_Split(A.col) B

-- 使用 OUTER APPLY

SELECT *

FROM @tb A

OUTER APPLY dbo.f_Split(A.col) B

GO

-- 刪除演示資料

DROP FUNCTION dbo.f_Split

/*

(4 行受影響)

col id val

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------- ----------

1,2,3 1 1

1,2,3 2 2

1,2,3 3 3

1 1 1

(4 行受影響)

col id val

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------- ----------

1,2,3 1 1

1,2,3 2 2

1,2,3 3 3

NULL NULL NULL

NULL NULL

1 1 1

(6 行受影響)

*/

GPS平臺、網站建設、軟體開發、系統運維,找森大網路科技!
https://cnsendnet.taobao.com
來自森大科技官方部落格
http://www.cnsendblog.com/index.php/?p=2029