Мати "АБО" у ВНУТРІШНЬОМУ ПРИЄДНАННІ погана ідея?


94

Намагаючись покращити швидкість надзвичайно повільного запиту (кілька хвилин на двох таблицях із лише ~ 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: гарна ідея. Виявляється, плани виконання показують лише те, що згадує Quassnoi нижче: перший запит призводить до вкладених циклів, тоді як другий виконується з хеш-з'єднанням.
ladenedge

Відповіді:


114

Цей вид 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

4

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


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

Бо СОЮЗ буде правий. Детальніше читайте наступне посилання union-вместо-or-or
Mitul Panchal

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