sql >> Base de Datos >  >> RDS >> Database

Por qué las UNIONES Múltiples son malas para la consulta o no se interponen en el camino del Optimizador

Recientemente, me encontré con una aplicación que generaba consultas de base de datos. Entiendo que no hay nada nuevo en eso, pero cuando la aplicación comenzó a funcionar lentamente y tuve que averiguar el motivo de la ralentización, me sorprendió encontrar estas consultas. Esto es con lo que SQL Server a veces tiene que lidiar:

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

Se han cambiado los nombres de los objetos.

Lo más llamativo fue que la misma tabla se usó varias veces, y la cantidad de paréntesis simplemente me volvió loco. No fui el único al que no le gustó este código, SQL Server tampoco lo apreció y gastó muchos recursos para crear un plan para él. La consulta puede ejecutarse entre 50 y 150 ms y la creación del plan puede demorar hasta 2,5 ms. Hoy, no consideraré las formas de solucionar el problema, pero diré una cosa:en mi caso, fue imposible solucionar la generación de consultas en la aplicación.

En su lugar, me gustaría analizar las razones por las que SQL Server crea el plan de consulta durante tanto tiempo. En cualquier DBMS, incluido SQL Server, el principal problema de optimización es el método de unión de tablas entre sí. Además del método de unión, la secuencia de unión de tablas es muy importante.

Hablemos de la secuencia de combinaciones de tablas en detalle. Es muy importante comprender que el número posible de combinaciones de tablas crece exponencialmente, no linealmente. Ejemplo de Fox, solo hay 2 métodos posibles para unir 2 tablas, y el número puede llegar a 12 métodos para 3 tablas. Diferentes secuencias de unión pueden tener un costo de consulta diferente, y el optimizador de SQL Server debe seleccionar el método más óptimo. Pero cuando el número de mesas es alto, se convierte en una tarea que requiere muchos recursos. Si SQL Server comienza a revisar todas las variantes posibles, es posible que dicha consulta nunca se ejecute. Por eso, SQL Server nunca lo hace y siempre busca un plan bastante bueno, no el mejor. SQL Server siempre intenta llegar a un compromiso entre el tiempo de ejecución y la calidad del plan.

Aquí hay un ejemplo del crecimiento exponencial de los métodos de combinación. SQL Server puede seleccionar varios métodos de unión (árboles espesos, profundos a la izquierda, profundos a la derecha). Visualmente, se ve de la siguiente manera:

La siguiente tabla muestra los posibles métodos de unión cuando aumenta el número de mesas:

Puede obtener estos valores por su cuenta:

Para izquierda-profundo: 5! =5 x 4 x 3 x 2 x 1 =120

Para árbol frondoso: (2n–2)!/(n–1)!

Conclusión :Preste especial atención a la cantidad de JOIN y no se interponga en el camino con el optimizador. Si no obtiene el resultado deseado en la consulta que contiene múltiples JOIN, divídalo en varias consultas pequeñas y se sorprenderá con el resultado.

PD Por supuesto, debemos entender que además de definir una secuencia de combinaciones de tablas, el optimizador de consultas también debe seleccionar el tipo de combinación, el método de acceso a los datos (Escanear, Buscar), etc.

Productos útiles:

SQL Complete:escribe, embellece, refactoriza tu código fácilmente y aumenta tu productividad.