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

时间:2018-01-26 11:05:46

标签: sql-server tsql 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

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

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

2 个答案:

答案 0 :(得分:3)

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

foreach ($itemSpec as $v){
     $allItemSpec = explode(':', $v);

     if($allItemSpec[0] == "Country"){
     $allItemSpec[0] = "Country/Region of 
       Manufacture";
       }
      $allItemSpec = "<tr>"
       "<td>$allItemSpec[0]:</td>
      <td>$allItemSpec[1]</td>
        </tr>";
       echo $allItemSpec;
      }

用OP自己的话说:

不是你这样做的方式,因为从概念上讲,你在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 之后用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'); 获取了包含NULL的行,但没有OUTER的行。

答案 1 :(得分:2)

CROSS APPLY视为与INNER JOINOUTER APPLY相关,与LEFT JOIN相关。

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

不同之处在于JOINs在关系条件下链接两个结果集,而APPLY在行方向上调用当前行的值。

您可以使用APPLY从行值中创建计算值或(以此为主要目的)以行值作为参数调用TVF。通常,您可以看到与APPLY相关的XMLTypedVariable.nodes()

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