创建笛卡尔积时 CROSS APPLY 和 OUTER APPLY 有什么区别吗?
Is there any difference between CROSS APPLY and OUTER APPLY when creating a cartesian product?
在两个table之间创建笛卡尔积时,CROSS APPLY
和OUTER APPLY
之间有什么区别吗?
这可能看起来像一个愚蠢的问题,因为如果 table 之间没有表达关系,右手 table 不能不满足关系,但我很尊重我不知道的。
当我查看具有简单测试设置的执行计划时,它们是相同的[两个索引寻求馈入嵌套循环(内连接)],但简单的测试设置可能具有欺骗性。
这是我的意思的一个例子 (SQL Fiddle)。设置:
CREATE TABLE dbo.First (
Id INT IDENTITY(1, 1) PRIMARY KEY,
Name NVARCHAR(100)
);
GO
DECLARE @n INT = 1;
WHILE @n < 10000
BEGIN
INSERT INTO dbo.First (Name) VALUES ('First' + CONVERT(NVARCHAR(100), @n));
SET @n = @n + 1;
END
GO
CREATE INDEX IX__First__Name ON dbo.First(Name);
GO
CREATE TABLE dbo.Second (
Id INT IDENTITY(1, 1) PRIMARY KEY,
Name NVARCHAR(100)
);
GO
DECLARE @n INT = 1;
WHILE @n < 10000
BEGIN
INSERT INTO dbo.Second (Name) VALUES ('Second' + CONVERT(NVARCHAR(100), @n));
SET @n = @n + 1;
END
GO
CREATE INDEX IX__Second__Name ON dbo.Second(Name);
GO
使用CROSS APPLY
:
SELECT First.Id AS FirstId, Second.Id AS SecondId
FROM First
CROSS APPLY Second
WHERE First.Name IN ('First253', 'First3304')
AND Second.Name IN ('Second6543', 'Second517');
使用OUTER APPLY
:
SELECT First.Id AS FirstId, Second.Id AS SecondId
FROM First
OUTER APPLY Second -- <== Only change is here
WHERE First.Name IN ('First253', 'First3304')
AND Second.Name IN ('Second6543', 'Second517');
...两者都给我预期的四行。
加上各种变体,其中一个或两个 IN
子句 return 不匹配:
-- No match in First
SELECT First.Id AS FirstId, Second.Id AS SecondId
FROM First
CROSS APPLY Second
WHERE First.Name IN ('no match')
AND Second.Name IN ('Second6543', 'Second517');
SELECT First.Id AS FirstId, Second.Id AS SecondId
FROM First
OUTER APPLY Second
WHERE First.Name IN ('no match')
AND Second.Name IN ('Second6543', 'Second517');
-- No match in Second
SELECT First.Id AS FirstId, Second.Id AS SecondId
FROM First
CROSS APPLY Second
WHERE First.Name IN ('First253', 'First3304')
AND Second.Name IN ('no match');
SELECT First.Id AS FirstId, Second.Id AS SecondId
FROM First
OUTER APPLY Second
WHERE First.Name IN ('First253', 'First3304')
AND Second.Name IN ('no match');
-- No match in either
SELECT First.Id AS FirstId, Second.Id AS SecondId
FROM First
CROSS APPLY Second
WHERE First.Name IN ('no match')
AND Second.Name IN ('no match');
SELECT First.Id AS FirstId, Second.Id AS SecondId
FROM First
OUTER APPLY Second
WHERE First.Name IN ('no match')
AND Second.Name IN ('no match');
...所有这些都给我预期的零行。
当应用 table 或 table 值函数没有记录时,差异开始发挥作用:
SELECT First.Id AS FirstId, Second.Id AS SecondId
FROM First
OUTER APPLY (SELECT * FROM Second WHERE Second.Id = -1) Second
WHERE First.Name IN ('First253', 'First3304');
2 rows returned
SELECT First.Id AS FirstId, Second.Id AS SecondId
FROM First
CROSS APPLY (SELECT * FROM Second WHERE Second.Id = -1) Second
WHERE First.Name IN ('First253', 'First3304');
0 rows returned
OP自己的话:
不是您这样做的方式,因为从概念上讲,您是在 APPLY
之后使用 WHERE
进行过滤(尽管计划显示引擎通过首先进行优化);但是如果你先明确过滤然后 APPLY
像这样:
SELECT First.Id AS FirstId, FilteredSecond.Id AS SecondId
FROM First
CROSS APPLY (SELECT Id FROM Second WHERE Name IN ('xxx')) FilteredSecond
WHERE First.Name IN ('First253', 'First3304');
您会看到不同之处,因为您会得到包含 OUTER
的 NULL 行,但没有包含 CROSS
.
的行
认为 CROSS APPLY
与 INNER JOIN
相关,OUTER APPLY
与 LEFT JOIN
相关。
- CROSS / INNER 会将结果限制为来自两个来源的行,而
如果第二个源中没有相关行,- OUTER / LEFT 将 return 第一个 table 和
NULLs
的所有行。
不同之处在于 JOINs
link 两个结果集基于一个关系条件,而 APPLY
被称为行方式,具有 当前行的值 .
您可以使用 APPLY
从行值中创建计算值,或者(这是主要目的)以行值作为参数调用 TVF。通常你可以看到 APPLY
与 XMLTypedVariable.nodes()
.
关于执行的一些思考
在简单的情况下(如上),引擎将决定走相同的路径。但是对于更复杂的场景,差异可能会很大。
在两个table之间创建笛卡尔积时,CROSS APPLY
和OUTER APPLY
之间有什么区别吗?
这可能看起来像一个愚蠢的问题,因为如果 table 之间没有表达关系,右手 table 不能不满足关系,但我很尊重我不知道的。
当我查看具有简单测试设置的执行计划时,它们是相同的[两个索引寻求馈入嵌套循环(内连接)],但简单的测试设置可能具有欺骗性。
这是我的意思的一个例子 (SQL Fiddle)。设置:
CREATE TABLE dbo.First (
Id INT IDENTITY(1, 1) PRIMARY KEY,
Name NVARCHAR(100)
);
GO
DECLARE @n INT = 1;
WHILE @n < 10000
BEGIN
INSERT INTO dbo.First (Name) VALUES ('First' + CONVERT(NVARCHAR(100), @n));
SET @n = @n + 1;
END
GO
CREATE INDEX IX__First__Name ON dbo.First(Name);
GO
CREATE TABLE dbo.Second (
Id INT IDENTITY(1, 1) PRIMARY KEY,
Name NVARCHAR(100)
);
GO
DECLARE @n INT = 1;
WHILE @n < 10000
BEGIN
INSERT INTO dbo.Second (Name) VALUES ('Second' + CONVERT(NVARCHAR(100), @n));
SET @n = @n + 1;
END
GO
CREATE INDEX IX__Second__Name ON dbo.Second(Name);
GO
使用CROSS APPLY
:
SELECT First.Id AS FirstId, Second.Id AS SecondId
FROM First
CROSS APPLY Second
WHERE First.Name IN ('First253', 'First3304')
AND Second.Name IN ('Second6543', 'Second517');
使用OUTER APPLY
:
SELECT First.Id AS FirstId, Second.Id AS SecondId
FROM First
OUTER APPLY Second -- <== Only change is here
WHERE First.Name IN ('First253', 'First3304')
AND Second.Name IN ('Second6543', 'Second517');
...两者都给我预期的四行。
加上各种变体,其中一个或两个 IN
子句 return 不匹配:
-- No match in First
SELECT First.Id AS FirstId, Second.Id AS SecondId
FROM First
CROSS APPLY Second
WHERE First.Name IN ('no match')
AND Second.Name IN ('Second6543', 'Second517');
SELECT First.Id AS FirstId, Second.Id AS SecondId
FROM First
OUTER APPLY Second
WHERE First.Name IN ('no match')
AND Second.Name IN ('Second6543', 'Second517');
-- No match in Second
SELECT First.Id AS FirstId, Second.Id AS SecondId
FROM First
CROSS APPLY Second
WHERE First.Name IN ('First253', 'First3304')
AND Second.Name IN ('no match');
SELECT First.Id AS FirstId, Second.Id AS SecondId
FROM First
OUTER APPLY Second
WHERE First.Name IN ('First253', 'First3304')
AND Second.Name IN ('no match');
-- No match in either
SELECT First.Id AS FirstId, Second.Id AS SecondId
FROM First
CROSS APPLY Second
WHERE First.Name IN ('no match')
AND Second.Name IN ('no match');
SELECT First.Id AS FirstId, Second.Id AS SecondId
FROM First
OUTER APPLY Second
WHERE First.Name IN ('no match')
AND Second.Name IN ('no match');
...所有这些都给我预期的零行。
当应用 table 或 table 值函数没有记录时,差异开始发挥作用:
SELECT First.Id AS FirstId, Second.Id AS SecondId
FROM First
OUTER APPLY (SELECT * FROM Second WHERE Second.Id = -1) Second
WHERE First.Name IN ('First253', 'First3304');
2 rows returned
SELECT First.Id AS FirstId, Second.Id AS SecondId
FROM First
CROSS APPLY (SELECT * FROM Second WHERE Second.Id = -1) Second
WHERE First.Name IN ('First253', 'First3304');
0 rows returned
OP自己的话:
不是您这样做的方式,因为从概念上讲,您是在 APPLY
之后使用 WHERE
进行过滤(尽管计划显示引擎通过首先进行优化);但是如果你先明确过滤然后 APPLY
像这样:
SELECT First.Id AS FirstId, FilteredSecond.Id AS SecondId
FROM First
CROSS APPLY (SELECT Id FROM Second WHERE Name IN ('xxx')) FilteredSecond
WHERE First.Name IN ('First253', 'First3304');
您会看到不同之处,因为您会得到包含 OUTER
的 NULL 行,但没有包含 CROSS
.
认为 CROSS APPLY
与 INNER JOIN
相关,OUTER APPLY
与 LEFT JOIN
相关。
- CROSS / INNER 会将结果限制为来自两个来源的行,而 如果第二个源中没有相关行,
- OUTER / LEFT 将 return 第一个 table 和
NULLs
的所有行。
不同之处在于 JOINs
link 两个结果集基于一个关系条件,而 APPLY
被称为行方式,具有 当前行的值 .
您可以使用 APPLY
从行值中创建计算值,或者(这是主要目的)以行值作为参数调用 TVF。通常你可以看到 APPLY
与 XMLTypedVariable.nodes()
.
关于执行的一些思考 在简单的情况下(如上),引擎将决定走相同的路径。但是对于更复杂的场景,差异可能会很大。