Имеет ли "OR" в состоянии INNER JOIN плохую идею?

При попытке улучшить скорость чрезвычайно медленного запроса (несколько минут на двух таблицах с количеством ~ 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 в условие соединения? Или мне просто не повезло в макете моих таблиц?

Ответ 1

Этот тип JOIN не оптимизирован для HASH JOIN или 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

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

Ответ 2

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


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)

Ответ 3

Вместо этого вы можете использовать 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