Разница в производительности между левым соединением и внутренним соединением

Есть ли разница между левым соединением и внутренним соединением относительно производительности? Я использую SQL Server 2012.

Ответ 1

Существует, по крайней мере, один случай, когда 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

Планы выполнения для двух последних запросов: enter image description here

Примечание 1/Вид 1: Если мы посмотрим на план выполнения для SELECT SalesOrderDetailID FROM View1 мы видим исключение FK, потому что ограничение 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.

Ответ 2

Как и проблема, что внешнее объединение может вернуть больший результирующий набор из-за того, что дополнительные строки сохраняются, еще одна точка заключается в том, что оптимизатор имеет больший диапазон возможностей при создании плана выполнения, поскольку 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.

Plans

Это преобразование неверно для внешнего соединения и вложенных циклов поддерживает только левое внешнее соединение, а не внешнее внешнее соединение, поэтому ему нужно использовать другое тип соединения.

Но с практической точки зрения вам нужно просто выбрать тип соединения, который вам нужен, что даст вам правильную семантику.