左连接和内连接在性能方面有什么区别吗?我使用的是SQL Server 2012。
答案 0 :(得分:11)
至少有一种情况,LEFT [OUTER] JOIN
是比[INNER] JOIN
更好的选择。我说的是使用OUTER
代替INNER
来获得相同的结果。
示例(我正在使用AdventureWorks 2008 database):
-- 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 elimination,因为FK_SalesOrderDetail_SalesOrderHeader_SalesOrderID
约束是受信任的,并且它只有一列。但是,服务器被强制(因为INNER JOIN Sales.SpecialOfferProduct
)从第三个表(SpecialOfferProduct)读取数据,即使SELECT/WHERE
子句不包含此表中的任何列,并且FK约束(FK_SalesOrderDetail_SpecialOfferProduct_SpecialOfferIDProductID)是(也)信任。发生这种情况是因为最后一个FK是多列的。
注意2 /视图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
表。
答案 1 :(得分:7)
除了外部联接可能返回更大的结果集的问题,因为保留了额外的行,另一点是优化器在创建执行计划时具有更大的可能性,因为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
是相同的,并生成一个带有嵌套循环的计划,以寻找表B
。
此转换对外连接和嵌套循环only supports left outer join not right outer join无效,因此需要使用不同的连接类型。
但是从实际的角度来看,你应该选择你需要的连接类型,它将为你提供正确的语义。