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

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


person mehdi lotfi    schedule 24.02.2013    source источник
comment
Да, может быть разница. Но почему тебя это волнует? Эти 2 не эквивалентны и (обычно) дают разные результаты. Итак, используйте тот, который полезен для вашего запроса и дает вам желаемые результаты.   -  person ypercubeᵀᴹ    schedule 24.02.2013
comment
возможный дубликат производительности INNER JOIN vs LEFT JOIN в SQL Server   -  person ypercubeᵀᴹ    schedule 24.02.2013


Ответы (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 является многоколоночным.

Примечание 2 / представление 2: Что, если мы хотим удалить чтение (_11 _ / _ 12_) на Sales.SpecialOfferProduct? Этот второй FK является многоколоночным, и в таких случаях SQL Server не может удалить FK (см. Предыдущее сообщение в блоге Конора Каннигэма). В этом случае нам нужно заменить INNER JOIN Sales.SpecialOfferProduct на LEFT OUTER JOIN Sales.SpecialOfferProduct, чтобы получить исключение FK. И SpecialOfferID, и ProductID столбцы NOT NULL, и у нас есть доверенный FK, ссылающийся на SpecialOfferProduct таблицу.

person Bogdan Sahlean    schedule 24.02.2013
comment
@MartinSmith: сейчас я добавляю несколько примечаний, но в этом случае они имеют одинаковую семантику. - person Bogdan Sahlean; 24.02.2013
comment
У них одинаковая семантика, только если есть FOREIGN KEY из SalesOrderDetail, ссылающийся на SpecialOfferProduct. - person ypercubeᵀᴹ; 24.02.2013
comment
@ypercube: FK_SalesOrderDetail_SpecialOfferProduct_SpecialOfferIDProductID - person Bogdan Sahlean; 24.02.2013
comment
Спасибо за терпение :-) - person Bogdan Sahlean; 24.02.2013
comment
Хороший. Существует страница подключения, на которой можно проголосовать за добавление функции: Поддержка исключения соединения для ограничений внешнего ключа с несколькими столбцами - person ypercubeᵀᴹ; 24.02.2013
comment
И еще одно связанное сообщение в блоге: Когда оценка строки идет неправильно - person ypercubeᵀᴹ; 24.02.2013
comment
@ ypercubeᵀᴹ - они имеют одинаковую семантику только в том случае, если есть ИНОСТРАННЫЙ КЛЮЧ от SalesOrderDetail, ссылающийся на SpecialOfferProduct, и ИНОСТРАННЫЙ КЛЮЧ НЕ НУЛЬ - person dbenham; 20.02.2020

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

Планы

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

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

person Martin Smith    schedule 24.02.2013