我创建了一个SQL命令,在9个表上使用INNER JOIN,无论如何这个命令需要很长时间(超过5分钟)。所以我的朋友建议我把INNER JOIN改为LEFT JOIN,因为LEFT JOIN的性能更好,尽管我知道。经过我的修改,查询的速度得到了显著的提高。

我想知道为什么LEFT JOIN比INNER JOIN快?

我的SQL命令如下所示: Select * from a inner join b on…内部连接c在…内部连接D等等

更新: 这是我图式的简介。

FROM sidisaleshdrmly a -- NOT HAVE PK AND FK
    INNER JOIN sidisalesdetmly b -- THIS TABLE ALSO HAVE NO PK AND FK
        ON a.CompanyCd = b.CompanyCd 
           AND a.SPRNo = b.SPRNo 
           AND a.SuffixNo = b.SuffixNo 
           AND a.dnno = b.dnno
    INNER JOIN exFSlipDet h -- PK = CompanyCd, FSlipNo, FSlipSuffix, FSlipLine
        ON a.CompanyCd = h.CompanyCd
           AND a.sprno = h.AcctSPRNo
    INNER JOIN exFSlipHdr c -- PK = CompanyCd, FSlipNo, FSlipSuffix
        ON c.CompanyCd = h.CompanyCd
           AND c.FSlipNo = h.FSlipNo 
           AND c.FSlipSuffix = h.FSlipSuffix 
    INNER JOIN coMappingExpParty d -- NO PK AND FK
        ON c.CompanyCd = d.CompanyCd
           AND c.CountryCd = d.CountryCd 
    INNER JOIN coProduct e -- PK = CompanyCd, ProductSalesCd
        ON b.CompanyCd = e.CompanyCd
           AND b.ProductSalesCd = e.ProductSalesCd 
    LEFT JOIN coUOM i -- PK = UOMId
        ON h.UOMId = i.UOMId 
    INNER JOIN coProductOldInformation j -- PK = CompanyCd, BFStatus, SpecCd
        ON a.CompanyCd = j.CompanyCd
            AND b.BFStatus = j.BFStatus
            AND b.ProductSalesCd = j.ProductSalesCd
    INNER JOIN coProductGroup1 g1 -- PK = CompanyCd, ProductCategoryCd, UsedDepartment, ProductGroup1Cd
        ON e.ProductGroup1Cd  = g1.ProductGroup1Cd
    INNER JOIN coProductGroup2 g2 -- PK = CompanyCd, ProductCategoryCd, UsedDepartment, ProductGroup2Cd
        ON e.ProductGroup1Cd  = g2.ProductGroup1Cd

当前回答

通过比较,我发现他们有完全相同的执行计划。有三种情况:

If and when they return the same results, they have the same speed. However, we must keep in mind that they are not the same queries, and that LEFT JOIN will possibly return more results (when some ON conditions aren't met) --- this is why it's usually slower. When the main table (first non-const one in the execution plan) has a restrictive condition (WHERE id = ?) and the corresponding ON condition is on a NULL value, the "right" table is not joined --- this is when LEFT JOIN is faster. As discussed in Point 1, usually INNER JOIN is more restrictive and returns fewer results and is therefore faster.

两者都使用(相同的)索引。

其他回答

外部连接在视图中使用时可以提供更好的性能。

假设您有一个涉及视图的查询,该视图由10个表连接在一起组成。假设您的查询恰好只使用这10个表中的3个表中的列。

如果这10个表是内部连接在一起的,那么查询优化器就必须将它们全部连接起来,即使您的查询本身并不需要10个表中的7个。这是因为内部连接本身可能会过滤数据,使它们成为计算的必要条件。

如果这10个表是外部连接在一起的,那么查询优化器实际上只会连接必要的表:在本例中是10个表中的3个。这是因为连接本身不再过滤数据,因此可以跳过未使用的连接。

来源: http://www.sqlservercentral.com/blogs/sql_coach/2010/07/29/poor-little-misunderstood-views/

在最后使用OPTION (FORCE ORDER)尝试两个查询(具有内连接和左连接的查询)并发布结果。OPTION (FORCE ORDER)是一个查询提示,它强制优化器使用您在查询中提供的连接顺序构建执行计划。

如果INNER JOIN开始和LEFT JOIN一样快,这是因为:

在完全由INNER join组成的查询中,连接顺序并不重要。这给了查询优化器以它认为合适的顺序排列连接的自由,因此问题可能依赖于优化器。 对于LEFT JOIN,情况并非如此,因为改变连接顺序将改变查询的结果。这意味着引擎必须遵循您在查询中提供的连接顺序,这可能比优化后的顺序更好。

不知道这是否回答了你的问题,但我曾经在一个项目中,具有高度复杂的查询进行计算,这完全搞砸了优化器。我们遇到过这样的情况,FORCE ORDER可以将查询的执行时间从5分钟减少到10秒。

LEFT JOIN绝对不会比INNER JOIN快。事实上,它更慢;根据定义,外部连接(LEFT join或RIGHT join)必须完成INNER join的所有工作,以及对结果进行空扩展的额外工作。由于结果集的大小更大,它还可能返回更多的行,从而进一步增加总执行时间。

(即使由于一些难以想象的因素的汇合,LEFT JOIN在特定情况下更快,但它在功能上并不等同于INNER JOIN,因此您不能简单地用一个实例替换另一个实例!)

您的性能问题很可能存在于其他地方,比如没有正确索引候选键或外键。9张桌子是相当多的加入,所以放缓可能几乎在任何地方。如果你发布你的模式,我们可能会提供更多的细节。


编辑:

进一步思考这个问题,我可以想到一种情况,在这种情况下左连接可能比内连接更快,那就是:

有些表格非常小(比如不到10行); 表没有足够的索引来覆盖查询。

想想这个例子:

CREATE TABLE #Test1
(
    ID int NOT NULL PRIMARY KEY,
    Name varchar(50) NOT NULL
)
INSERT #Test1 (ID, Name) VALUES (1, 'One')
INSERT #Test1 (ID, Name) VALUES (2, 'Two')
INSERT #Test1 (ID, Name) VALUES (3, 'Three')
INSERT #Test1 (ID, Name) VALUES (4, 'Four')
INSERT #Test1 (ID, Name) VALUES (5, 'Five')

CREATE TABLE #Test2
(
    ID int NOT NULL PRIMARY KEY,
    Name varchar(50) NOT NULL
)
INSERT #Test2 (ID, Name) VALUES (1, 'One')
INSERT #Test2 (ID, Name) VALUES (2, 'Two')
INSERT #Test2 (ID, Name) VALUES (3, 'Three')
INSERT #Test2 (ID, Name) VALUES (4, 'Four')
INSERT #Test2 (ID, Name) VALUES (5, 'Five')

SELECT *
FROM #Test1 t1
INNER JOIN #Test2 t2
ON t2.Name = t1.Name

SELECT *
FROM #Test1 t1
LEFT JOIN #Test2 t2
ON t2.Name = t1.Name

DROP TABLE #Test1
DROP TABLE #Test2

如果运行该命令并查看执行计划,您将看到INNER JOIN查询的开销确实比LEFT JOIN多,因为它满足上面的两个条件。这是因为SQL Server想要为INNER JOIN做一个哈希匹配,但为LEFT JOIN做了嵌套循环;前者通常要快得多,但由于行数非常少,而且没有索引可使用,因此散列操作是查询中开销最大的部分。

你可以用你最喜欢的编程语言编写一个程序,对一个只有5个元素的列表执行大量的查找,而不是对一个只有5个元素的哈希表执行大量的查找,从而看到同样的效果。由于大小的原因,哈希表版本实际上更慢。但是将其增加到50个元素,或者5000个元素,列表版本就会变慢,因为哈希表是O(N) vs. O(1)。

但是将此查询更改为ID列,而不是Name列,您将看到非常不同的情况。在这种情况下,它为两个查询都做了嵌套循环,但是INNER JOIN版本能够用seek替换其中一个聚集索引扫描—这意味着在大量行的情况下,这实际上会快一个数量级。

结论和我前面几段提到的差不多;这几乎肯定是一个索引或索引覆盖问题,可能与一个或多个非常小的表结合在一起。在这些情况下,SQL Server有时可能会为INNER JOIN选择比LEFT JOIN更糟糕的执行计划。

如果一切都按照它应该的方式工作,那就不应该,但是我们都知道一切都不是按照它应该的方式工作,特别是当涉及到查询优化器、查询计划缓存和统计时。

首先,我建议重新构建索引和统计数据,然后清除查询计划缓存,以确保不会搞砸事情。然而,即使这样做了,我也遇到了问题。

我经历过一些左连接比内连接更快的情况。

The underlying reason is this: If you have two tables and you join on a column with an index (on both tables). The inner join will produce the same result no matter if you loop over the entries in the index on table one and match with index on table two as if you would do the reverse: Loop over entries in the index on table two and match with index in table one. The problem is when you have misleading statistics, the query optimizer will use the statistics of the index to find the table with least matching entries (based on your other criteria). If you have two tables with 1 million in each, in table one you have 10 rows matching and in table two you have 100000 rows matching. The best way would be to do an index scan on table one and matching 10 times in table two. The reverse would be an index scan that loops over 100000 rows and tries to match 100000 times and only 10 succeed. So if the statistics isn't correct the optimizer might choose the wrong table and index to loop over.

如果优化器选择按照左连接的编写顺序优化它,那么它将比内部连接执行得更好。

但是,优化器也可以将左连接次优化为左半连接。要让它选择你想要的,你可以使用强制顺序提示。

我在SQL server中发现了一些有趣的东西,当检查内部连接是否比左连接更快时。

如果你不包括左连接表的项,在选择语句中,左连接将比使用内连接的相同查询更快。

如果在选择语句中包含左连接表,则具有相同查询的内部连接与左连接相同或更快。