LEFT JOIN Значительно быстрее, чем INNER JOIN

У меня есть таблица (MainTable) с бит более 600 000 записей. Он присоединяется к себе через вторую таблицу (JoinTable) в отношении отношения родительского/дочернего типа:

SELECT   Child.ID, Parent.ID
FROM     MainTable
AS       Child
JOIN     JoinTable
      ON Child.ID = JoinTable.ID
JOIN     MainTable
AS       Parent
      ON Parent.ID = JoinTable.ParentID
     AND Parent.SomeOtherData = Child.SomeOtherData

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

Когда я запускаю этот запрос, он занимает буквально минуты для запуска. Однако, если я присоединяюсь к Parent, используя Left Join, тогда он принимает < 1 секунда для запуска:

SELECT   Child.ID, Parent.ID
FROM     MainTable
AS       Child
JOIN     JoinTable
      ON Child.ID = JoinTable.ID
LEFT JOIN MainTable
AS       Parent
      ON Parent.ID = JoinTable.ParentID
     AND Parent.SomeOtherData = Child.SomeOtherData
WHERE    ...[some info to make sure we don't select parent records in the child dataset]...

Я понимаю разницу в результатах между INNER JOIN и a LEFT JOIN. В этом случае он возвращает точно такой же результат, как и каждый ребенок имеет родителя. Если я позволю обоим запросам запустить, я смогу сравнить наборы данных, и они будут точно такими же.

Почему он LEFT JOIN работает намного быстрее, чем INNER JOIN?


UPDATE Проверяли планы запросов, и при использовании внутреннего соединения он начинается с набора данных родителя. При выполнении левого соединения начинается с набора данных для детей.

Используемые индексы одинаковы.

Могу ли я заставить его всегда начинать с ребенка? Используя левое соединение, он просто чувствует себя не так.


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

например. выбранный ответ в INNER JOIN vs LEFT JOIN производительности в SQL Server говорит, что Left Joins всегда медленнее, чем Inner join. Аргумент имеет смысл, но это не то, что я вижу.

Ответ 1

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

3 способа заставить соединения произойти в правильном порядке:

  • Выберите первое подмножество данных во временную таблицу (или переменную таблицы), затем присоедините к ней
  • Используйте левые соединения (и помните, что это может возвращать разные данные, потому что левое соединение не является внутренним соединением)
  • используйте ключевое слово FORCE ORDER. Обратите внимание, что если размеры таблиц или схемы меняются, то план запроса может быть неправильным (см. https://dba.stackexchange.com/questions/45388/forcing-join-order)

Ответ 2

Попробуйте это. Тот же результат, другой подход:

SELECT c.ID, p.ID 
FROM
(SELECT   Child.ID, JoinTable.ParentID
FROM     MainTable
AS       Child
JOIN     JoinTable
      ON Child.ID = JoinTable.ID) AS c
INNER JOIN 
(SELECT   Parent.ID, JoinTable.ID
FROM     MainTable
AS       Parent
JOIN     JoinTable
      ON Parent.ID = JoinTable.ParentID
     AND Parent.SomeOtherData = Child.SomeOtherData) AS p
ON c.ParentID = p.ID

Если это не помогает, используйте cte:

;WITH cte AS
(SELECT   Child.ID, JoinTable.ParentID
FROM     MainTable
AS       Child
JOIN     JoinTable
      ON Child.ID = JoinTable.ID)
SELECT cte.ID, Parent.ID
FROM cte INNER JOIN 
MainTable
AS       Parent
      ON Parent.ID = cte.ParentID
     AND Parent.SomeOtherData = cte.SomeOtherData