Próbując poprawić szybkość niezwykle powolnego zapytania (kilka minut na dwóch tabelach zawierających tylko ~ 50 000 wierszy w każdej, w SQL Server 2008, jeśli ma to znaczenie), zawęziłem problem do połączenia OR
wewnętrznego, na przykład:
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
Zmieniłem to na (mam nadzieję) równoważną parę lewych złączeń, pokazaną tutaj:
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
.. a zapytanie działa teraz za około sekundę!
Czy wprowadzenie OR
warunku złączenia jest ogólnie złym pomysłem ? A może po prostu mam pecha w układzie moich stołów?
sql
sql-server
sql-server-2008
tsql
inner-join
ładenedge
źródło
źródło
Odpowiedzi:
Tego rodzaju
JOIN
nie można zoptymalizować do aHASH JOIN
lub aMERGE JOIN
.Można to wyrazić jako konkatenację dwóch zestawów wyników:
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
jednak każdy z nich jest equijoinem, jednak
SQL Server
optymalizator nie jest wystarczająco inteligentny, aby zobaczyć go w zapytaniu, które napisałeś (chociaż są one logicznie równoważne).źródło
ON w=x OR y=z
całkowicie unikać łączenia wzorca?ON w=x OR y=z
? (Dzięki za cierpliwość!)SQL Server
zrozumieć, że konieczne będzie połączenie. Powiedzmy, że zapytanieSELECT * FROM othertable WHERE parentId = 1 OR id = 2
użyje konkatenacji, jeśli oba pola są indeksowane, więc teoretycznie nie ma nic, co uniemożliwiłoby zrobienie tego samego w pętli. ToSQL Server
, czy ten plan faktycznie zbuduje, czy nie, zależy od bardzo wielu czynników, ale nigdy nie widziałem go zbudowanego w prawdziwym życiu.Używam następującego kodu, aby uzyskać inny wynik niż warunek, który zadziałał dla mnie.
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)
źródło
Zamiast tego możesz użyć 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
źródło
UNION ALL
da ci powiela w porównaniu doJOIN
zOR
warunku.union all
co nie jest poprawne, ponieważ artykuł, do którego prowadzi link, również opisuje.