Является ли использование «ИЛИ» в условии INNER JOIN плохой идеей?


96

Пытаясь повысить скорость чрезвычайно медленного запроса (несколько минут для двух таблиц, содержащих всего ~ 50 000 строк в каждой, на SQL Server 2008, если это имеет значение), я сузил проблему до ORвнутреннего соединения, например:

SELECT mt.ID, mt.ParentID, ot.MasterID
  FROM dbo.MainTable AS mt
  INNER JOIN dbo.OtherTable AS ot ON ot.ParentID = mt.ID
                                  OR ot.ID = mt.ParentID

Я изменил это на (что я надеюсь) эквивалентную пару левых соединений, показанную здесь:

SELECT mt.ID, mt.ParentID,
   CASE WHEN ot1.MasterID IS NOT NULL THEN
      ot1.MasterID ELSE
      ot2.MasterID END AS MasterID
  FROM dbo.MainTable AS mt
  LEFT JOIN dbo.OtherTable AS ot1 ON ot1.ParentID = mt.ID
  LEFT JOIN dbo.OtherTable AS ot2 ON ot2.ID = mt.ParentID
  WHERE ot1.MasterID IS NOT NULL OR ot2.MasterID IS NOT NULL

.. и теперь запрос выполняется примерно за секунду!

Является ли вообще плохой идеей помещать объект ORв условие соединения? Или мне просто как-то не повезло с раскладкой столов?


6
Покажите нам план выполнения вместо вашего запроса.
Blindy

кажутся странными отношениями
Натан Гонсалес

@Blindy: хорошая идея. Оказывается, планы выполнения показывают именно то, что Квассной упоминает ниже: первый запрос приводит к вложенным циклам, а второй выполняется с хеш-соединением.
ladenedge

Ответы:


115

Этот тип JOINнельзя оптимизировать для a HASH JOINили a MERGE JOIN.

Это можно выразить как объединение двух наборов результатов:

SELECT  *
FROM    maintable m
JOIN    othertable o
ON      o.parentId = m.id
UNION
SELECT  *
FROM    maintable m
JOIN    othertable o
ON      o.id = m.parentId

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


3
в этом есть смысл, спасибо. Я до сих пор не уверен, есть ли что-то особенное в моем запросе или мне следует полностью избегать объединений ON w=x OR y=zшаблона?
ladenedge

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

просто для ясности, когда вы говорите «эти соединения», вы имеете в виду все соединения формы ON w=x OR y=z? (Спасибо за терпение!)
ladenedge

3
@ladenedge: могут быть дополнительные условия, которые помогут SQL Serverпонять, что потребуется конкатенация. Скажем, запрос SELECT * FROM othertable WHERE parentId = 1 OR id = 2будет использовать конкатенацию, если оба поля проиндексированы, поэтому теоретически нет ничего, что могло бы помешать сделать то же самое в цикле. Будет ли SQL Serverстроить этот план на самом деле или нет, зависит от очень многих факторов, но я никогда не видел, чтобы он был реализован в реальной жизни.
Quassnoi

Также обратите внимание, что если вы знаете, что они являются непересекающимися наборами, объединение ALL может значительно улучшить производительность, избегая объединения наборов результатов.
Даррен Кларк,

5

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


Select A.column, B.column
FROM TABLE1 A
INNER JOIN
TABLE2 B
ON A.Id = (case when (your condition) then b.Id else (something) END)

-2

Вместо этого вы можете использовать UNION ALL.

SELECT mt.ID, mt.ParentID, ot.MasterID FROM dbo.MainTable AS mt Union ALL SELECT mt.ID, mt.ParentID, ot.MasterID FROM dbo.OtherTable AS ot


UNION ALLдаст вам дубликаты по сравнению JOINс с ORусловием.
CodeMonkey

Для этого СОЮЗ будет прав. Для получения более подробной информации прочитайте следующую ссылку профсоюзом вместо-о-или
Mitul Panchal

1
да, но в вашем примере вы написали это union allнеправильно, поскольку статья, на которую вы ссылаетесь, также описывает.
CodeMonkey
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.