Database
 sql >> Database >  >> RDS >> Database

Perché più JOIN sono dannosi per le query o non intralciano l'ottimizzatore

Di recente, mi sono imbattuto in un'applicazione che generava query DB. Capisco che non ci sia nulla di nuovo in questo, ma quando l'applicazione ha iniziato a funzionare lentamente e ho dovuto scoprire il motivo del rallentamento, sono rimasto stupito di trovare queste query. Ecco cosa deve affrontare SQL Server a volte:

SELECT COUNT(DISTINCT "pr"."id") FROM  ((((((((((((((((("SomeTable" "pr"
LEFT OUTER JOIN "SomeTable1698" "uf_pr_id_698" ON "uf_pr_id_698"."request" = "pr"."id") 
LEFT OUTER JOIN "SomeTable1700" "ufref3737_i2" ON "ufref3737_i2"."request" = "pr"."id") 
LEFT OUTER JOIN "SomeTable1666" "x0" ON "x0"."request" = "ufref3737_i2"."f6_callerper")
LEFT OUTER JOIN "SomeTable1666" "uf_ufref4646_i3_f58__666" ON "uf_ufref4646_i3_f58__666"."request" = "ufref3737_i2"."f58_")
LEFT OUTER JOIN "SomeTable1694" "x1" ON "x1"."request" = "ufref3737_i2"."f38_servicep")
LEFT OUTER JOIN "SomeTable3754" "ufref3754_i12" ON "pr"."id" = "ufref3754_i12"."request")
LEFT OUTER JOIN "SomeTable1698" "uf_ufref3754_i12_reference_698" ON "uf_ufref3754_i12_reference_698"."request" = "ufref3754_i12"."reference")
LEFT OUTER JOIN "SomeTable1698" "x2" ON "x2"."request" = "ufref3737_i2"."f34_parentse")
LEFT OUTER JOIN "SomeTable4128" "ufref3779_4128_i14" ON "ufref3737_i2"."f34_parentse" = "ufref3779_4128_i14"."request")
LEFT OUTER JOIN "SomeTable1859" "x3" ON "x3"."request" = "ufref3779_4128_i14"."reference")
LEFT OUTER JOIN "SomeTable3758" "ufref3758_i15" ON "pr"."id" = "ufref3758_i15"."request")
LEFT OUTER JOIN "SomeTable1698" "uf_ufref3758_i15_reference_698" ON "uf_ufref3758_i15_reference_698"."request" = "ufref3758_i15"."reference")
LEFT OUTER JOIN "SomeTable3758" "ufref3758_i16" ON "pr"."id" = "ufref3758_i16"."request")
LEFT OUTER JOIN "SomeTable4128" "ufref3758_4128_i16" ON "ufref3758_i16"."reference" = "ufref3758_4128_i16"."request")
LEFT OUTER JOIN "SomeTable1859" "x4" ON "x4"."request" = "ufref3758_4128_i16"."reference")
LEFT OUTER JOIN "SomeTable4128" "ufref4128_i17" ON "pr"."id" = "ufref4128_i17"."request")
LEFT OUTER JOIN "SomeTable1859" "uf_ufref4128_i17_reference_859" ON "uf_ufref4128_i17_reference_859"."request" = "ufref4128_i17"."reference")
LEFT OUTER JOIN "SomeTable1666" "uf_ufref4667_i25_f69__666" ON "uf_ufref4667_i25_f69__666"."request" = "uf_pr_id_698"."f69_"
WHERE ("uf_pr_id_698"."f1_applicant" IN (248,169,180,201,203,205,209,215,223,357,371,379,3502,3503,3506,3514,3517,3531,3740,3741)
OR "x0"."f24_useracco" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136)
OR "uf_ufref4646_i3_f58__666"."f24_useracco" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136)
OR "uf_ufref4667_i25_f69__666"."f24_useracco" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136)
OR ("uf_pr_id_698"."f10_status" Is Null OR "uf_pr_id_698"."f10_status" <> 111)  AND "ufref3737_i2"."f96_" = 0   AND (("ufref3737_i2"."f17_source"  Is Null OR "ufref3737_i2"."f17_source"  <> 566425)
AND ("ufref3737_i2"."f17_source"  Is Null OR "ufref3737_i2"."f17_source"  <> 566424)  OR ("uf_pr_id_698"."f10_status" Is Null OR "uf_pr_id_698"."f10_status" <> 56) )
AND ("uf_pr_id_698"."f12_responsi" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136)
OR "x1"."f19_restrict" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136)
OR "uf_ufref3754_i12_reference_698"."f12_responsi" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136)
OR "x2"."f12_responsi" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136)
OR "x3"."f5_responsib" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136) 
OR "uf_ufref3758_i15_reference_698"."f12_responsi" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136) 
OR "x4"."f5_responsib" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136)
OR "uf_ufref4128_i17_reference_859"."f5_responsib" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136))
AND ("uf_pr_id_698"."f12_responsi"  Is Null OR "uf_pr_id_698"."f12_responsi"  <> 579420)  ) AND "pr"."area" IN (700) AND "pr"."area" IN (700) AND "pr"."deleted_by_user"=0 AND "pr"."temporary" = 0

I nomi degli oggetti sono stati modificati.

La cosa più sorprendente è che la stessa tabella è stata utilizzata più volte e il numero di parentesi mi ha semplicemente fatto impazzire. Non sono stato l'unico a cui non è piaciuto questo codice, SQL Server non lo ha apprezzato e ha speso molte risorse per creare un piano per esso. La query può essere eseguita per 50-150 ms e la creazione del piano può richiedere fino a 2,5 ms. Oggi non prenderò in considerazione i modi per risolvere il problema, ma dirò una cosa:nel mio caso, era impossibile correggere la generazione di query nell'applicazione.

Invece, vorrei analizzare i motivi per cui SQL Server crea il piano di query per così tanto tempo. In qualsiasi DBMS, incluso SQL Server, il principale problema di ottimizzazione è il metodo di unione delle tabelle tra loro. Oltre al metodo di join, la sequenza di join delle tabelle è molto importante.

Parliamo in dettaglio della sequenza di join delle tabelle. È molto importante capire che il numero possibile di join di tabelle cresce in modo esponenziale, non lineare. Ad esempio Fox, ci sono solo 2 metodi possibili per unire 2 tavoli e il numero può raggiungere 12 metodi per 3 tavoli. Sequenze di join diverse possono avere costi di query diversi e l'ottimizzatore di SQL Server deve selezionare il metodo più ottimale. Ma quando il numero di tabelle è elevato, diventa un'attività ad alta intensità di risorse. Se SQL Server inizia a esaminare tutte le possibili varianti, tale query potrebbe non essere mai eseguita. Ecco perché SQL Server non lo fa mai e cerca sempre un piano abbastanza buono, non il migliore. SQL Server cerca sempre di raggiungere un compromesso tra il tempo di esecuzione e la qualità del piano.

Ecco un esempio della crescita esponenziale dei metodi di join. SQL Server può selezionare vari metodi di unione (alberi profondi a sinistra, profondi a destra, cespugliosi). Visivamente, si presenta nel modo seguente:

La tabella seguente mostra i possibili metodi di unione all'aumentare del numero di tabelle:

Puoi ottenere questi valori da solo:

Per profondo sinistro: 5! =5 x 4 x 3 x 2 x 1 =120

Per albero cespuglioso: (2n–2)!/(n–1)!

Conclusione :Presta particolare attenzione al numero di JOIN e non intralciare con l'ottimizzatore. Se non riesci a ottenere il risultato desiderato nella query contenente più JOIN, suddividilo in diverse piccole query e rimarrai sorpreso dal risultato.

PS Ovviamente, dobbiamo capire che oltre a definire una sequenza di join di tabelle, Query Optimizer deve anche selezionare il tipo di join, il metodo di accesso ai dati (Scansione, Ricerca) ecc.

Prodotti utili:

SQL Complete:scrivi, abbellisci, refactoring facilmente il tuo codice e aumenta la tua produttività.