TL; DR
Poiché questa domanda continua a ricevere visualizzazioni, la riassumerò qui in modo che i nuovi arrivati non debbano subire la storia:
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
Mi rendo conto che questo potrebbe non essere un problema per tutti, ma evidenziando la sensibilità delle clausole ON, potrebbe aiutarti a guardare nella giusta direzione. In ogni caso il testo originale è qui per i futuri antropologi:
Testo originale
Considera la seguente semplice query (coinvolte solo 3 tabelle)
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
Questa è una query piuttosto semplice, l'unica parte confusa è l'ultimo join di categoria, è così perché il livello di categoria 5 potrebbe o non potrebbe esistere. Alla fine della query sto cercando informazioni sulla categoria per ID prodotto (SKU ID), ed è qui che entra in gioco la tabella molto grande category_link. Infine, la tabella #Ids è solo una tabella temporanea contenente 10'000 ID.
Quando eseguito, ottengo il seguente piano di esecuzione effettivo:
Come puoi vedere, quasi il 90% del tempo viene speso nei loop annidati (Inner Join). Ecco ulteriori informazioni su quei loop annidati:
Tieni presente che i nomi delle tabelle non corrispondono esattamente perché ho modificato i nomi delle tabelle delle query per renderle leggibili, ma è abbastanza facile abbinarli (ads_alt_category = categoria). Esiste un modo per ottimizzare questa query? Inoltre, in produzione, la tabella temporanea #Ids non esiste, è un parametro con valori di tabella degli stessi 10'000 ID passati alla Stored Procedure.
Informazioni addizionali:
- indici di categoria su category_id e parent_category_id
- indice category_voc su ID_categoria, codice_ lingua
- indice category_link su sku_id, category_id
Modifica (risolto)
Come sottolineato dalla risposta accettata, il problema era la clausola OR nel JOIN di category_link. Tuttavia, il codice suggerito nella risposta accettata è molto lento, anche più lento del codice originale. Una soluzione molto più veloce e anche molto più pulita è semplicemente quella di sostituire l'attuale condizione JOIN con quanto segue:
JOIN category_link l on l.sku_id IN (SELECT value FROM @p1) AND l.category_id = COALESCE(c5.category_id, c4.category_id)
Questa piccola modifica è la soluzione più veloce, testata contro il doppio join dalla risposta accettata e testata anche con CROSS APPLY come suggerito da valverij.