TL; DR
Da diese Frage immer wieder auftaucht, fasse ich sie hier zusammen, damit Neulinge die Geschichte nicht leiden müssen:
JOIN table t ON t.member = @value1 OR t.member = @value2 -- this is slow as hell
JOIN table t ON t.member = COALESCE(@value1, @value2) -- this is blazing fast
-- Note that here if @value1 has a value, @value2 is NULL, and vice versa
Mir ist klar, dass dies möglicherweise nicht jedermanns Problem ist, aber wenn Sie die Empfindlichkeit der ON-Klauseln hervorheben, hilft dies Ihnen möglicherweise, in die richtige Richtung zu schauen. In jedem Fall ist der Originaltext hier für zukünftige Anthropologen:
Original Text
Betrachten Sie die folgende einfache Abfrage (nur 3 betroffene Tabellen)
SELECT
l.sku_id AS ProductId,
l.is_primary AS IsPrimary,
v1.category_name AS Category1,
v2.category_name AS Category2,
v3.category_name AS Category3,
v4.category_name AS Category4,
v5.category_name AS Category5
FROM category c4
JOIN category_voc v4 ON v4.category_id = c4.category_id and v4.language_code = 'en'
JOIN category c3 ON c3.category_id = c4.parent_category_id
JOIN category_voc v3 ON v3.category_id = c3.category_id and v3.language_code = 'en'
JOIN category c2 ON c2.category_id = c3.category_id
JOIN category_voc v2 ON v2.category_id = c2.category_id and v2.language_code = 'en'
JOIN category c1 ON c1.category_id = c2.parent_category_id
JOIN category_voc v1 ON v1.category_id = c1.category_id and v1.language_code = 'en'
LEFT OUTER JOIN category c5 ON c5.parent_category_id = c4.category_id
LEFT OUTER JOIN category_voc v5 ON v5.category_id = c5.category_id and v5.language_code = @lang
JOIN category_link l on l.sku_id IN (SELECT value FROM #Ids) AND
(
l.category_id = c4.category_id OR
l.category_id = c5.category_id
)
WHERE c4.[level] = 4 AND c4.version_id = 5
Dies ist eine ziemlich einfache Abfrage. Der einzige verwirrende Teil ist der letzte Kategorie-Join. Dies ist der Fall, da Kategorie-Level 5 möglicherweise vorhanden ist oder nicht. Am Ende der Abfrage suche ich nach Kategoriedaten pro Produkt-ID (SKU-ID) und hier kommt die sehr große Tabelle category_link ins Spiel. Schließlich ist die Tabelle #Ids nur eine temporäre Tabelle mit 10'000 IDs.
Bei der Ausführung erhalte ich den folgenden tatsächlichen Ausführungsplan:
Wie Sie sehen, werden fast 90% der Zeit in den verschachtelten Schleifen (Inner Join) verbracht. Hier sind zusätzliche Informationen zu diesen verschachtelten Schleifen:
Beachten Sie, dass die Tabellennamen nicht genau übereinstimmen, da ich die Abfragetabellennamen aus Gründen der Lesbarkeit bearbeitet habe, die Zuordnung jedoch recht einfach ist (ads_alt_category = category). Gibt es eine Möglichkeit, diese Abfrage zu optimieren? Beachten Sie auch, dass in der Produktion die temporäre Tabelle #Ids nicht existiert, sondern ein Tabellenwertparameter mit denselben 10'000 IDs ist, die an die gespeicherte Prozedur weitergeleitet wurden.
Zusätzliche Information:
- Kategorieindizes für category_id und parent_category_id
- category_voc-Index für category_id, language_code
- category_link-Index für sku_id, category_id
Bearbeiten (gelöst)
Wie aus der akzeptierten Antwort hervorgeht, war das Problem die OR-Klausel im category_link JOIN. Der in der akzeptierten Antwort vorgeschlagene Code ist jedoch sehr langsam und sogar langsamer als der ursprüngliche Code. Eine viel schnellere und auch viel sauberere Lösung besteht einfach darin, die aktuelle JOIN-Bedingung durch Folgendes zu ersetzen:
JOIN category_link l on l.sku_id IN (SELECT value FROM @p1) AND l.category_id = COALESCE(c5.category_id, c4.category_id)
Diese minuziöse Optimierung ist die schnellste Lösung, die anhand der Doppelverknüpfung aus der akzeptierten Antwort und anhand der von valverij vorgeschlagenen KREUZANWENDUNG getestet wurde.