除了CROSS JOIN, INNER JOIN, OUTER JOIN之外,T-SQL還提供了CROSS APPLY和OUTER APPLY這兩個較爲另類的Set操作符。
首先來看CROSS APPLY。跟CROSS JOIN一樣,MSDN只在FROM Clause的文檔中做了一個介紹,如下:
Both the left and right operands of the APPLY operator are table expressions. The main difference between these operands is that the right_table_source can use a table-valued function that takes a column from the left_table_source as one of the arguments of the function. The left_table_source can include table-valued functions, but it cannot contain arguments that are columns from the right_table_source.
The APPLY operator works in the following way to produce the table source for the FROM clause:
Evaluates right_table_source against each row of the left_table_source to produce rowsets.
The values in the right_table_source depend on left_table_source. right_table_source can be represented approximately this way: TVF(left_table_source.row), where TVF is a table-valued function.
Combines the result sets that are produced for each row in the evaluation of right_table_source with the left_table_source by performing a UNION ALL operation.The list of columns produced by the result of the APPLY operator is the set of columns from the left_table_source that is combined with the list of columns from the right_table_source.
簡單來說就是,APPLY操作符的過程就是:
- 計算左表表達式
- 將左表表達式結果作爲右表輸入
通常,對右表表達式,即可以是表,也可以爲Function。
例一,右表表達式爲Function,左表中名爲Tags的Column保存了多個Key拼接而成的字符串。如Tags爲“abc, def, acd”
-- Define functions
if object_id('parsetags','TF') is not null
drop function parsetags;
GO
create function parsetags(
@tags nvarchar(1000),
@splits varchar(10)
)
returns @t_tags TABLE (tag nvarchar(100))
as
begin
set @tags = RTrim(LTrim(@tags))
set @i = CharIndex(@splits,@tags)
while @i >= 1
begin
insert @t_tags Values(Left(@tags,@i-1))
set @tags = SubString(@tags,@i+1,Len(@tags)-@i)
set @i = CharIndex(@Splits,@tags)
end
if @tags <> ''
insert @t_tags Values (@tags)
return;
end
GO
-- Define table t_blog
CREATE TABLE t_blog
(
blogid INT NOT NULL,
blogcontent NVARCHAR(MAX) NOT NULL,
tags NVARCHAR(200) NOT NULL
)
GO
-- Example of CROSS APPLY
SELECT * FROM t_blog CROSS APPLY parsetags(t_blog.tags, ';')
GO
例二,右表表達式爲另外一張表,選出每個Customer最大的兩筆Sales訂單。
-- Create Customer table
CREATE TABLE t_customer
(
[id] NVARCHAR(50) NOT NULL,
[name] NVARCHAR(50) NOT NULL
)
GO
-- Create Sales table
CREATE TABLE t_sales
(
[id] NVARCHAR(50) NOT NULL,
[custid] NVARCHAR(50) NOT NULL,
[amount] MONEY NOT NULL
)
GO
-- Using cross apply
SELECT t_customer.[id] as custid, t_customer.[name] as custname, sales.[id] as saleid, sales.[amount] as salesamount
FROM t_customer
CROSS APPLY
(SELECT top 2 *
FROM t_sales
WHERE t_customer.id = t_sales.custid
ORDER BY amount) AS sales
GO
以上都是使用CROSS APPLY做例子,而OUTER APPLY與其的主要區別是,OUTER APPLY會左表表達式中存在而右表表達式運算結果爲NULL的項目,跟INNER JOIN跟OUTER JOIN的概念完全一致。
用例二作爲說明:
- 使用CROSS APPLY時,沒有任何Sales的Customer在結果集中不顯示;
- 用OUTER APPLY時,沒有Sales的Customer也會在結果集中出現,但其對應的saleid和saleamount都爲NULL
是爲之記。
Alva Chien
2016.6.14