Есть ли разница между левым соединением и внутренним соединением относительно производительности? Я использую 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
Планы выполнения для двух последних запросов:
Примечание 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
.
Это преобразование неверно для внешнего соединения и вложенных циклов поддерживает только левое внешнее соединение, а не внешнее внешнее соединение, поэтому ему нужно использовать другое тип соединения.
Но с практической точки зрения вам нужно просто выбрать тип соединения, который вам нужен, что даст вам правильную семантику.