6

CROSS APPLY在两个表之间创建笛卡尔积时,和之间有什么区别OUTER 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

使用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子句都不返回匹配项:

-- 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');

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

4

2 回答 2

3

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

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 自己的话来说:

不是您这样做的方式,因为从概念上讲,您是在进行过滤WHEREAPPLY尽管计划显示引擎通过先进行优化);但是如果你先明确过滤然后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');

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

于 2018-01-26T11:26:12.070 回答
2

认为与 aCROSS APPLY相关INNER JOIN并且OUTER APPLY与 a 相关LEFT JOIN

  • CROSS / INNER 会将结果限制为来自两个来源的行,而
  • OUTER / LEFT 将返回第一个表的所有行,NULLs如果第二个源中没有相关行。

不同之处在于JOINs通过关系条件链接两个结果集,而使用当前行APPLY的值按行调用。

您可以使用APPLY从行值创建计算值或(这是主要目的)以行值作为参数调用 TVF。通常你可以看到APPLYXMLTypedVariable.nodes().

关于执行的一些想法 在简单的情况下(如上),引擎将决定走相同的路径。但是对于更复杂的场景,差异可能会很大。

于 2018-01-26T11:26:42.857 回答