3

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

+4

Да, может быть разница. Но почему вас это волнует? Эти 2 не эквивалентны и будут (обычно) давать разные результаты. Итак, используйте тот, который полезен для вашего запроса и дает нужные вам результаты. –

+0

Возможный дубликат [INNER JOIN vs LEFT JOIN performance в SQL Server] (http://stackoverflow.com/questions/2726657/inner-join-vs-left-join-performance-in-sql-server) –

ответ

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 

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

Примечание 1/Вид 1: Если мы посмотрим на план выполнения для SELECT SalesOrderDetailID FROM View1 мы видим FK elimination, потому что ограничение FK_SalesOrderDetail_SalesOrderHeader_SalesOrderID доверено и имеет один столбец. Но сервер вынужден (из-за) читать данные из третьей таблицы (SpecialOfferProduct), даже предложения SELECT/WHERE не содержат никаких столбцов из этой таблицы, а также ограничение FK (FK_SalesOrderDetail_SpecialOfferProduct_SpecialOfferIDProductID) доверено. Это происходит потому, что последний FK является многоколоночным.

Примечание 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.

+0

@MartinSmith: Я добавляю несколько заметок, но ** в этом случае ** они имеют одинаковую семантику. –

+0

Они имеют одинаковую семантику, только если есть «FOREIGN KEY» из «SalesOrderDetail», ссылающегося на «SpecialOfferProduct». –

+0

@ypercube: 'FK_SalesOrderDetail_SpecialOfferProduct_SpecialOfferIDProductID' –

6

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

Это преобразование не является действительным для внешнего соединения и вложенность only supports left outer join not right outer join поэтому необходимо использовать другой тип соединения.

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

Смежные вопросы