左连接和内连接在性能方面有什么区别吗?我使用 SQL Server 2012。
2 回答
至少有一种情况LEFT [OUTER] JOIN
比[INNER] JOIN
. 我谈论使用OUTER
而不是获得相同的结果INNER
。
示例(我使用AdventureWorks 2008 数据库):
-- Some metadata infos
SELECT fk.is_not_trusted, fk.name
FROM sys.foreign_keys fk
WHERE fk.parent_object_id=object_id('Sales.SalesOrderDetail');
GO
CREATE VIEW View1
AS
SELECT h.OrderDate, d.SalesOrderDetailID, o.ModifiedDate
FROM Sales.SalesOrderDetail d
INNER JOIN Sales.SalesOrderHeader h ON d.SalesOrderID = h.SalesOrderID
INNER JOIN Sales.SpecialOfferProduct o ON d.SpecialOfferID=o.SpecialOfferID AND d.ProductID=o.ProductID;
GO
CREATE VIEW View2
AS
SELECT h.OrderDate, d.SalesOrderDetailID, o.ModifiedDate
FROM Sales.SalesOrderDetail d
INNER JOIN Sales.SalesOrderHeader h ON d.SalesOrderID = h.SalesOrderID
LEFT JOIN Sales.SpecialOfferProduct o ON d.SpecialOfferID=o.SpecialOfferID AND d.ProductID=o.ProductID;
GO
SELECT SalesOrderDetailID
FROM View1;
SELECT SalesOrderDetailID
FROM View2;
第一个查询的结果:
is_not_trusted name
-------------- ---------------------------------------------------------------
0 FK_SalesOrderDetail_SalesOrderHeader_SalesOrderID
0 FK_SalesOrderDetail_SpecialOfferProduct_SpecialOfferIDProductID
最后两个查询的执行计划:
注 1 / 视图 1:如果我们查看执行计划,SELECT SalesOrderDetailID FROM View1
我们会看到FK 消除,因为FK_SalesOrderDetail_SalesOrderHeader_SalesOrderID
约束是可信的并且它只有一列。但是,服务器被迫(因为INNER JOIN Sales.SpecialOfferProduct
)从第三个表(SpecialOfferProduct)读取数据,即使SELECT/WHERE
子句不包含该表中的任何列,并且 FK 约束(FK_SalesOrderDetail_SpecialOfferProduct_SpecialOfferIDProductID)(也)是受信任的。发生这种情况是因为最后一个 FK 是多列的。
Note 2 / View 2:如果我们想删除读(Scan
/ Seek
)上的Sales.SpecialOfferProduct
怎么办?第二个 FK 是多列的,对于这种情况,SQL Server 无法消除 FK(请参阅之前的 Conor Cunnigham 博客文章)。在这种情况下,我们需要将 替换为INNER JOIN Sales.SpecialOfferProduct
以LEFT OUTER JOIN Sales.SpecialOfferProduct
消除 FK。SpecialOfferID
和ProductID
列都是,NOT NULL
我们有一个受信任的 FK 引用SpecialOfferProduct
表。
由于保留了额外的行,外连接可能返回更大的结果集的另一个问题是优化器在创建执行计划时具有更大范围的可能性,因为INNER JOIN
它是可交换和关联的。
因此,对于以下示例B
,已编入索引但未编入索引A
。
CREATE TABLE A(X INT, Filler CHAR(8000))
INSERT INTO A
SELECT TOP 10000 ROW_NUMBER() OVER (ORDER BY @@SPID), ''
FROM sys.all_columns
CREATE TABLE B(X INT PRIMARY KEY, Filler CHAR(8000))
INSERT INTO B
SELECT TOP 10000 ROW_NUMBER() OVER (ORDER BY @@SPID), ''
FROM sys.all_columns
SELECT *
FROM B INNER JOIN A ON A.X = B.X
SELECT *
FROM B LEFT JOIN A ON A.X = B.X
优化器知道B INNER JOIN A
和 A INNER JOIN B
是相同的,并生成一个带有嵌套循环的计划,用于查找 table B
。
此转换对外连接无效,嵌套循环仅支持左外连接而不支持右外连接,因此需要使用不同的连接类型。
但是从实际的角度来看,您应该只选择您需要的连接类型,这将为您提供正确的语义。