创建笛卡尔积时,CROSS APPLY和OUTER APPLY之间有什么区别吗?

T.J*_*der 5 t-sql sql-server cross-apply outer-apply

当两个表之间创建一个笛卡尔积,有什么区别CROSS APPLYOUTER APPLY

这似乎是一个愚蠢的问题,因为没有表之间表达的关系,右手表不能不满足关系,但我尊重我不知道的.

当我通过简单的测试设置查看执行计划时,它们是相同的[两个索引寻求进入嵌套循环(内部联接)],但简单的测试设置可能具有欺骗性.

这是我的意思的一个例子(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
Run Code Online (Sandbox Code Playgroud)

使用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');
Run Code Online (Sandbox Code Playgroud)

使用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');
Run Code Online (Sandbox Code Playgroud)

......这两个都给了我预期的四行.

加上其中一个或两个IN子句不返回匹配项的各种变体:

-- 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');
Run Code Online (Sandbox Code Playgroud)

......所有这些都给了我预期的零行.

Y.B*_*.B. 4

当应用的表或表值函数没有记录时,差异就会发挥作用:

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
Run Code Online (Sandbox Code Playgroud)

用OP自己的话说:

不是你这样做的方式,因为从概念上讲,你WHERE在之后进行过滤APPLY(尽管计划显示引擎通过首先执行它来优化);但如果你先显式过滤然后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');
Run Code Online (Sandbox Code Playgroud)

您会看到差异,因为您会得到带有 NULL 的行,OUTER但没有带有 的行CROSS

  • 所以你的答案是“不是你这样做的方式,因为从概念上讲,你在‘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 的行。” 这基本上就是您所说的吗? (2认同)