Tengo una instrucción SQL UPDATE con una cláusula "TOP (X)", y la fila en la que estoy actualizando los valores tiene aproximadamente 4 mil millones de filas. Cuando uso "TOP (10)", obtengo un plan de ejecución que se ejecuta casi instantáneamente, pero cuando uso "TOP (50)" o más grande, la consulta nunca (al menos, no mientras espero) finaliza, y Utiliza un plan de ejecución completamente diferente. La consulta más pequeña usa un plan muy simple con un par de búsquedas de índice y una unión de bucle anidado, donde la misma consulta exacta (con un número diferente de filas en la cláusula TOP de la instrucción UPDATE) usa un plan que involucra dos búsquedas de índice diferentes , un carrete de mesa, paralelismo y un montón de otras complejidades.
He usado "OPTION (USE PLAN ...)" para forzarlo a usar el plan de ejecución generado por la consulta más pequeña; cuando hago esto, puedo actualizar hasta 100,000 filas en unos segundos. Sé que el plan de consulta es bueno, pero SQL Server solo elegirá ese plan por sí solo cuando solo intervenga un pequeño número de filas; cualquier recuento de filas decente en mi actualización dará como resultado un plan subóptimo.
Pensé que el paralelismo podría ser el culpable, así que puse MAXDOP 1
la consulta, pero sin ningún efecto: ese paso se ha ido, pero la mala elección / rendimiento no lo es. También corrí sp_updatestats
esta mañana para asegurarme de que esa no fuera la causa.
He adjuntado los dos planes de ejecución: el más corto también es el más rápido. Además, aquí está la consulta en cuestión (vale la pena señalar que el SELECT que he incluido parece ser rápido en los casos de recuento de filas grandes y pequeñas):
update top (10000) FactSubscriberUsage3
set AccountID = sma.CustomerID
--select top 50 f.AccountID, sma.CustomerID
from FactSubscriberUsage3 f
join dimTime t
on f.TimeID = t.TimeID
join #mac sma
on f.macid = sma.macid
and t.TimeValue between sma.StartDate and sma.enddate
where f.AccountID = 0 --There's a filtered index on the table for this
¿Hay algo obvio en la forma en que estoy configurando mi consulta o en el plan de ejecución siempre que se preste a la mala elección que está haciendo el motor de consulta? Si es necesario, también puedo incluir las definiciones de tabla involucradas y los índices que se definen en ellas.
Para aquellos que pidieron una versión de estadísticas de los objetos de la base de datos: ni siquiera me di cuenta de que podían hacer eso, ¡pero tiene mucho sentido! Intenté generar los scripts para una base de datos solo de estadísticas para que otros pudieran probar los planes de ejecución por sí mismos, pero puedo generar generar estadísticas / histogramas en mi índice filtrado (error de sintaxis en el script, parece), así que estoy sin suerte allí. Traté de eliminar el filtro y los planes de consulta estaban cerca, pero no exactamente lo mismo, y no quiero enviar a nadie en una persecución.
Actualización y algunos planes de ejecución más completos: en primer lugar, el Explorador de planes de SQL Sentry es una herramienta increíble. Ni siquiera sabía que existía hasta que vi las otras preguntas del plan de consulta en este sitio, y tenía bastante que decir sobre cómo se estaban ejecutando mis consultas. Aunque no estoy seguro de cómo abordar el problema, dejaron en claro cuál es el problema.
Aquí está el resumen de 10, 100 y 1000 filas: puede ver que la consulta de 1000 filas está muy fuera de línea con las demás:
Puede ver que la tercera consulta tiene un número ridículo de lecturas, por lo que obviamente está haciendo algo completamente diferente. Aquí está el plan de ejecución estimado, con recuentos de filas. Plan de ejecución estimado de 1000 filas:
Y aquí están los resultados reales del plan de ejecución (por cierto, por "nunca termina", resulta que quise decir "termina en una hora"). Plan de ejecución real de 1000 filas
La primera cosa que noté fue que, en vez de tirar 60K filas de la tabla DimTime como se espera, en realidad tirando de 1,6 mil millones, con un B . Mirando mi consulta, no estoy seguro de cómo está retirando tantas filas de la tabla dimTime. El operador ENTRE que estoy usando solo se asegura de que estoy sacando el registro correcto de #mac en función del registro de tiempo en la tabla de hechos. Sin embargo, cuando agrego una línea a la cláusula WHERE donde filtro t.TimeValue (o t.TimeID) a un solo valor, puedo actualizar con éxito 100.000 filas en cuestión de segundos. Como resultado de eso, y como se dejó en claro en los planes de ejecución que incluí, es obvio que es mi horario el problema, pero no estoy seguro de cómo cambiaría los criterios de combinación para solucionar este problema y mantener la precisión . ¿Alguna idea?
Como referencia, aquí el plan (con recuentos de filas) para la actualización de 100 filas. Puede ver que alcanza el mismo índice, y aún con una tonelada de filas, pero en ninguna parte cerca de la misma magnitud de un problema. Ejecución de 100 filas con recuento de filas :
sp_updatestatistics
sobre la mesa?from #mac sma join f on f.macid = sma.macid join dimTime t on f.TimeID = t.TimeID and t.TimeValue between sma.StartDate and sma.enddate
vsfrom #mac join t on t.TimeValue between sma.StartDate and sma.enddate join f on f.TimeID = t.TimeID and f.macid = sma.macid
TOP 50
aún debe ejecutarse rápidamente. ¿Puedes subir los planes XML? Necesito mirar los recuentos de filas. ¿Se puede ejecutarTOP 50
con maxdop 1 y como una selección, no como una actualización y publicar el plan? (Intentando simplificar / bisecar el espacio de búsqueda).t.TimeValue between sma.StartDate and sma.enddate
puede terminar generando muchas más filas inútiles que luego se filtran en la unión contra FactSubscriber y, por lo tanto, no terminan en el resultado final.Respuestas:
El índice en dimTime está cambiando. El plan más rápido está utilizando un índice _dta. En primer lugar, asegúrese de que no esté marcado como un índice hipotético en sys.indexes.
Pensando que podría pasar por alto alguna parametrización utilizando la tabla #mac para filtrar en lugar de simplemente proporcionar las fechas de inicio / finalización como esta DONDE t.TimeValue entre @StartDate y @enddate. Deshágase de esa tabla temporal.
fuente
t.TimeValue between sma.StartDate and sma.enddate
está correlacionado, por lo que puede cambiar para cada fila de la#temp
tabla. ¿Con qué lo reemplazaría el OP?Sin más información sobre los recuentos de filas en el plan, mi recomendación preliminar es organizar el orden de unión correcto en la consulta y forzarlo a usar
OPTION (FORCE ORDER)
. Aplicar el orden de unión del primer plan.fuente