我创建了一个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

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更糟糕的执行计划。

您的性能问题更有可能是由于您正在执行的连接数量以及您正在连接的列是否具有索引。

在最坏的情况下,您可能很容易为每个连接执行9个整表扫描。

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

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

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

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.

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

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

有一种重要的场景可能导致外部连接比内部连接更快,但我们还没有讨论过。

当使用外部连接时,如果连接列是外部表的PK,并且外部表列没有在外部连接本身之外被引用,优化器总是可以从执行计划中删除外部连接表。例如选择A.*从A.的左外连接B到A. key =B。KEY和B.KEY是B.的PK。Oracle(我相信我使用的是10版)和Sql Server(我使用的是2008 R2)都从执行计划中删除表B。

对于内部连接则不一定是这样:SELECT A.* FROM A内部连接B ON A. key =B。KEY在执行计划中可能需要B,也可能不需要,这取决于存在什么约束。

如果a . key是一个引用B. key的可空外键,那么优化器不能从计划中删除B,因为它必须确认每个a行都存在一个B行。

如果a . key是引用B. key的强制外键,那么优化器可以从计划中删除B,因为约束保证了行的存在。但是仅仅因为优化器可以从计划中删除表,并不意味着它会这样做。SQL Server 2008 R2不会将B从计划中删除。Oracle 10将B从计划中删除。在这种情况下,很容易看出SQL Server上的外部连接如何优于内部连接。

这是一个简单的示例,对于独立查询来说并不实际。如果不需要,为什么要加入一个表?

But this could be a very important design consideration when designing views. Frequently a "do-everything" view is built that joins everything a user might need related to a central table. (Especially if there are naive users doing ad-hoc queries that do not understand the relational model) The view may include all the relevent columns from many tables. But the end users might only access columns from a subset of the tables within the view. If the tables are joined with outer joins, then the optimizer can (and does) drop the un-needed tables from the plan.

确保使用外部连接的视图给出正确的结果是至关重要的。正如Aaronaught所说,你不能盲目地用OUTER JOIN代替INNER JOIN,并期望得到相同的结果。但在使用视图时,有时出于性能考虑,它可能很有用。

最后一个注意事项-我还没有测试上述对性能的影响,但从理论上讲,如果你还在where子句中添加条件<FOREIGN_KEY> IS NOT NULL,你应该可以安全地将INNER JOIN替换为OUTER JOIN。

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

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

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

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

Have done a number of comparisons between left outer and inner joins and have not been able to find a consisten difference. There are many variables. Am working on a reporting database with thousands of tables many with a large number of fields, many changes over time (vendor versions and local workflow) . It is not possible to create all of the combinations of covering indexes to meet the needs of such a wide variety of queries and handle historical data. Have seen inner queries kill server performance because two large (millions to tens of millions of rows) tables are inner joined both pulling a large number of fields and no covering index exists.

但最大的问题似乎并没有出现在上面的讨论中。也许您的数据库设计良好,具有触发器和设计良好的事务处理,以确保良好的数据。我的经常在不期望的地方有NULL值。是的,表定义可以强制no- null,但在我的环境中,这不是一个选项。

所以问题是…您设计查询时是否只考虑速度?对于每分钟运行相同代码数千次的事务处理来说,这是一个更高的优先级。或者你追求左外连接所提供的准确性。请记住,内部连接必须在两边都找到匹配项,因此意外的NULL不仅会从两个表中删除数据,而且可能会删除整行信息。它发生得很好,没有错误消息。

您可以非常快速地获得所需数据的90%,而不会发现内部连接已经无声地删除了信息。有时内部连接可以更快,但我不相信任何人做出这样的假设,除非他们已经审查了执行计划。速度固然重要,但准确更重要。

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

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

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

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

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

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

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

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

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

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.

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