SQL Server 2005 引入 APPLY 運算子,允許將表值函數 (TVF) 套用至外部查詢的每一列,解決「每列呼叫函數並展開結果」的問題。本文完整示範 CROSS APPLY (內部聯接語意) 與 OUTER APPLY (左外部聯接語意) 的差異、建立拆解逗號字串的 TVF、與 SQL 2000 游標寫法對比。
CROSS APPLY:僅回傳 TVF 有結果的列 (如 INNER JOIN)OUTER APPLY:回傳所有左輸入列,TVF 無結果時右側欄位為 NULL (如 LEFT JOIN)CREATE TABLE MyData (
ids INT IDENTITY PRIMARY KEY,
Data NVARCHAR(1000)
);
INSERT INTO MyData VALUES (''), ('a,b,c'), ('q'), ('i,p');
-- TVF:依逗號拆解字串,每段產生一列 (id, value)
CREATE FUNCTION dbo.fn_MyData(@data NVARCHAR(1000))
RETURNS @tem TABLE (id INT, value NVARCHAR(100))
AS
BEGIN
SET @data = ISNULL(@data, '');
IF LEN(@data) = 0 RETURN;
DECLARE @id INT = 1, @end INT = CHARINDEX(',', @data);
WHILE @end > 0
BEGIN
INSERT @tem SELECT @id, LEFT(@data, @end - 1);
SET @id += 1;
SET @data = RIGHT(@data, LEN(@data) - @end);
SET @end = CHARINDEX(',', @data);
END
IF LEN(@data) > 0 INSERT @tem SELECT @id, @data;
RETURN;
END
SELECT m.ids, f.*
FROM MyData m CROSS APPLY dbo.fn_MyData(m.Data) f;
ids id value
2 1 a
2 2 b
2 3 c
3 1 q
4 1 i
4 2 p
ids=1 (空字串) TVF 回傳空集 → 被過濾掉
SELECT m.ids, f.*
FROM MyData m OUTER APPLY dbo.fn_MyData(m.Data) f;
ids id value
1 NULL NULL -- 空字串列保留,右側為 NULL
2 1 a
2 2 b
2 3 c
3 1 q
4 1 i
4 2 p
DECLARE @ids INT, @data NVARCHAR(200);
DECLARE @tem TABLE (ids INT, id INT, value NVARCHAR(100));
DECLARE cur CURSOR FOR SELECT ids, Data FROM MyData;
OPEN cur;
FETCH NEXT FROM cur INTO @ids, @data;
WHILE @@FETCH_STATUS = 0
BEGIN
INSERT @tem SELECT @ids, id, value FROM dbo.fn_MyData(@data);
FETCH NEXT FROM cur INTO @ids, @data;
END
CLOSE cur; DEALLOCATE cur;
SELECT * FROM @tem;
SELECT c.*, o.*
FROM Customers c
CROSS APPLY (
SELECT TOP 1 * FROM Orders o
WHERE o.CustomerID = c.CustomerID
ORDER BY OrderDate DESC
) o;
-- SQL 2016+ STRING_SPLIT
SELECT c.*, s.value
FROM Customers c
CROSS APPLY STRING_SPLIT(c.Tags, ',') s;
-- XML 欄位拆解
SELECT c.*, x.c.value('.', 'NVARCHAR(100)')
FROM Customers c
CROSS APPLY c.Preferences.nodes('/prefs/item') x(c);
CREATE FUNCTION dbo.CalcCustomerMetrics(@CustID INT)
RETURNS TABLE
AS RETURN (
SELECT
@CustID AS CustomerID,
COUNT(*) AS OrderCount,
SUM(Amount) AS TotalAmount,
AVG(Amount) AS AvgAmount
FROM Orders WHERE CustomerID = @CustID
);
SELECT c.*, m.*
FROM Customers c
CROSS APPLY dbo.CalcCustomerMetrics(c.CustomerID) m;
BEGIN...RETURN) 為黑盒,最佳化器無法展開,大量資料效能較差RETURN SELECT ...,無變數/迴圈)APPLY 與 JOIN 最大差異:右側可引用左側欄位,JOIN 的 ON 子句無法呼叫 TVFLATERAL JOIN (等效功能)CROSS APPLY / OUTER APPLY