嵌套在OUTER JOIN内部的INNER JOIN语法与查询结果


10

TLDR;如果您查看这两个执行计划,有一个简单的答案是哪个更好?我故意没有创建索引,因此更容易看到正在发生的事情。

我之前的问题(我们发现不同的联接样式(即嵌套与传统)之间的查询性能差异)进行了跟进之后,我意识到嵌套语法也可以修改查询的行为。考虑以下两个查询。

SELECT  a.*, m.*, n.*
FROM    dbo.Autos a
LEFT JOIN dbo.Models m
  JOIN dbo.Manufacturers n  -- <-- Nested INNER JOIN
  ON n.ManufacturerID = m.ManufacturerID
ON m.ModelID = a.ModelID

在此处输入图片说明

这并不一定使制造商加入,以包括与ModelID自动行是不是在型号表。

在此处输入图片说明

使用传统语法,我们必须将Manufactures的联接更改为外部联接,就像这样……但这会更改查询计划。

SELECT a.*, m.*, n.*
FROM dbo.Autos a
LEFT JOIN dbo.Models m
ON m.ModelID = a.ModelID
LEFT JOIN dbo.Manufacturers n -- <-- Now LEFT OUTER JOIN
ON n.ManufacturerID = m.ManufacturerID

在此处输入图片说明

Answers:


12

如果您查看这两个执行计划,有一个简单的答案是哪个更好?我故意没有创建索引,因此更容易看到正在发生的事情。

第二个计划的估计成本较低,因此从有限的角度来看,它是“更好的”计划。

数据集是如此之小,以至于优化器没有花太多时间在寻找替代方案上。查询的第一种形式恰好是在早期使用哈希联接和表假脱机找到计划的。该计划的估计成本如此之低,以至于优化器不会费心寻找任何更好的东西。

查询的第二种形式恰好是在搜索过程的早期阶段仅使用嵌套循环外部联接来查找计划,然后优化程序再次确定该计划足够好。碰巧这种计划估计会更便宜。

也就是说(如问题注释中所述),这两个查询在语义上并不相同。如果可以保证对于数据库的所有将来状态,结果始终是相同的,那么这对您可能并不重要,但是优化器无法做出此假设。在所有情况下,它只会生成保证产生SQL指定的相同结果的计划。

我已经意识到,嵌套语法还可以修改查询的行为。

“嵌套语法”只是整个ANSI连接语法规范的一方面。为了对更复杂的连接模式启用完整的逻辑规范,该规范允许(可选)括号和FROM子句子查询。

可以使用括号使用相同的ANSI语法编写查询:

SELECT
    A.*,
    M.*,
    N.* 
FROM dbo.Autos AS A
LEFT JOIN
(
    dbo.Manufacturers AS N
    JOIN dbo.Models AS M
        ON M.ManufacturerID = N.ManufacturerID
) ON M.ModelID = A.ModelID;

这种形式清楚地表明,该逻辑的要求是从左连接Autos结果内接合ManufacturersModels。省略可选的括号将使您称之为“嵌套”形式:

SELECT
    A.*,
    M.*,
    N.* 
FROM dbo.Autos AS A
LEFT JOIN dbo.Manufacturers AS N
JOIN dbo.Models AS M
    ON M.ManufacturerID = N.ManufacturerID
    ON M.ModelID = A.ModelID;

这不是不同的语法-只是省略了可选的括号并重新格式化了一点。

正如Martin所提到的,在这种情况下,也可以使用内部联接,然后是右外部联接来表达逻辑要求:

SELECT
    A.*,
    M.*,
    N.* 
FROM dbo.Manufacturers AS N
JOIN dbo.Models AS M
    ON M.ManufacturerID = N.ManufacturerID
RIGHT JOIN dbo.Autos AS A
    ON A.ModelID = M.ModelID;

以上所有三种查询形式都使用相同的ANSI连接语法。这三者也碰巧使用提供的数据集产生了相同的物理执行计划:

共同执行计划

正如我在对上一个问题的回答中提到的那样,表达完全相同的逻辑要求的查询不一定会产生相同的执行计划。您更喜欢使用哪种逻辑查询形式,很大程度上取决于样式。通常,一种特定样式与“更好”的查询计划之间没有关联。如果新查询在逻辑上与原始查询并不完全相同,通常建议不要重写查询以获取特定计划。

SQL标准还允许FROM子句子查询,因此编写相同查询规范的另一种方法是:

SELECT * 
FROM dbo.Autos AS A
LEFT JOIN
(
    SELECT
        N.ManufacturerID,
        ManufacturerName = N.Name,
        M.ModelID,
        ModelName = M.Name
    FROM dbo.Manufacturers AS N
    JOIN dbo.Models AS M
        ON M.ManufacturerID = N.ManufacturerID
) AS R1
    ON R1.ModelID = A.ModelID;

使用传统语法,我们必须将`制造商的联接更改为外部联接,就像这样...,但这会更改查询计划。

这可能会更改查询的含义,在这种情况下,从技术上讲,这不是有效的选择(但请参见ypercube对您的问题的 评论)。

ANSI连接语法中的(可选)括号正是针对更复杂的连接要求,因此,您不必担心在必要时使用它们。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.