Tengo una consulta bastante simple
SELECT TOP 1 dc.DOCUMENT_ID,
dc.COPIES,
dc.REQUESTOR,
dc.D_ID,
cj.FILE_NUMBER
FROM DOCUMENT_QUEUE dc
JOIN CORRESPONDENCE_JOURNAL cj
ON dc.DOCUMENT_ID = cj.DOCUMENT_ID
WHERE dc.QUEUE_DATE <= GETDATE()
AND dc.PRINT_LOCATION = 2
ORDER BY cj.FILE_NUMBER
Eso me está dando un rendimiento horrible (como nunca molestarse en esperar a que termine). El plan de consulta se ve así:
Sin embargo, si elimino TOP 1
, obtengo un plan que se ve así y se ejecuta en 1-2 segundos:
Corregir PK e indexación a continuación.
El hecho de que haya TOP 1
cambiado el plan de consulta no me sorprende, solo estoy un poco sorprendido de que lo haga mucho peor.
Nota: He leído los resultados de esta publicación y entiendo el concepto de un Row Goal
etc. Lo que me interesa es cómo puedo cambiar la consulta para que utilice el mejor plan. Actualmente estoy volcando los datos en una tabla temporal y luego quitando la primera fila. Me pregunto si hay un mejor método.
Editar Para las personas que leen esto después del hecho, aquí hay algunas piezas adicionales de información.
- Document_Queue: PK / CI es D_ID y tiene ~ 5k filas.
- Correspondence_Journal: PK / CI es FILE_NUMBER, CORRESPONDENCE_ID y tiene ~ 1.4 mil filas.
Cuando comencé no había otros índices. Terminé con uno en Correspondence_Journal (Document_Id, File_Number)
fuente
DOCUMENT_ID
relación entre las dos tablas (o cada registroCORRESPONDENCE_JOURNAL
tiene un registro coincidenteDOCUMENT_QUEUE
)?Respuestas:
Intenta forzar un hash join *
El optimizador probablemente pensó que un bucle iba a ser mejor con el top 1 y eso tiene sentido, pero en realidad no funcionó aquí. Solo una suposición aquí, pero tal vez el costo estimado de ese carrete estaba apagado; usa TEMPDB; es posible que tenga un TEMPDB de bajo rendimiento.
* Tenga cuidado con las sugerencias de combinación , ya que fuerzan el orden de acceso a la tabla del plan para que coincida con el orden escrito de las tablas en la consulta (como si se
OPTION (FORCE ORDER)
hubiera especificado). Desde el enlace de documentación:Es posible que esto no produzca ningún efecto indeseable en el ejemplo, pero en general, podría muy bien.
FORCE ORDER
(implícito o explícito) es una pista muy poderosa que va más allá de imponer el orden; evita que se aplique una amplia gama de técnicas optimizadoras, incluidas agregaciones parciales y reordenamientos.Una sugerencia de
OPTION (HASH JOIN)
consulta puede ser menos intrusiva en casos adecuados, ya que esto no implicaFORCE ORDER
. Sin embargo, se aplica a todas las combinaciones en la consulta. Otras soluciones están disponibles.fuente
Dado que obtiene el plan correcto con el
ORDER BY
, ¿tal vez podría simplemente rodar su propioTOP
operador?En mi opinión, el plan de consulta para lo
ROW_NUMBER()
anterior debería ser el mismo que si tuviera unORDER BY
. El plan de consulta ahora debe tener un Segmento, Proyecto de secuencia y, finalmente, un operador de Filtro, el resto debe parecerse a su buen plan.fuente
Editar: +1 funciona en esta situación porque resulta que
FILE_NUMBER
es una versión de cadena con relleno de cero de un entero. Una mejor solución aquí para las cadenas es agregar''
(la cadena vacía), ya que agregar un valor puede afectar el orden, o para que los números agreguen algo que es una constante pero que contiene una función no determinista, comosign(rand()+1)
. La idea de 'romper el género' todavía es válida aquí, es solo que mi método no era ideal.+1
No, no quiero decir que estoy de acuerdo con nada, lo digo como una solución. Si cambia su consulta a,
ORDER BY cj.FILE_NUMBER + 1
entoncesTOP 1
se comportará de manera diferente.Verá, con el objetivo de fila pequeña establecido para una consulta ordenada, el sistema intentará consumir los datos en orden, para evitar tener un operador Ordenar. También evitará construir una tabla hash, ya que probablemente no tenga que trabajar demasiado para encontrar esa primera fila. En su caso, esto está mal: por el grosor de esas flechas, parece que tiene que consumir muchos datos para encontrar una sola coincidencia.
El grosor de esas flechas sugiere que su
DOCUMENT_QUEUE
tabla (DQ) es mucho más pequeña que suCORRESPONDENCE_JOURNAL
tabla (CJ). Y que el mejor plan sería verificar las filas DQ hasta encontrar una fila CJ. De hecho, eso es lo que haría el Optimizador de consultas (QO) si no tuviera este molestoORDER BY
, eso está bien respaldado por un índice de cobertura en CJ.Entonces, si dejaste caer el
ORDER BY
completo, espero que obtenga un plan que involucre un bucle anidado, iterando sobre las filas en DQ, buscando en CJ para asegurarse de que exista la fila. Y conTOP 1
esto, esto se detendría después de que una sola fila fuera retirada.Pero si realmente necesitas la primera fila en
FILE_NUMBER
orden, entonces podría engañar al sistema para que ignore ese índice que parece (incorrectamente) ser muy útil al hacerloORDER BY CJ.FILE_NUMBER+1
, lo que sabemos mantendrá el mismo orden que antes, pero lo más importante es que el QO no lo hace El QO se centrará en obtener todo el conjunto, para que un operador Top N Sort pueda estar satisfecho. Este método debe producir un plan que contenga un operador Compute Scalar para calcular el valor para ordenar y un operador Top N Sort para obtener la primera fila. Pero a la derecha de estos, debería ver un bonito Nested Loop, haciendo muchas búsquedas en CJ. Y un mejor rendimiento que ejecutar a través de una gran tabla de filas que no coinciden con nada en DQ.El Hash Match no es necesariamente horrible, pero si el conjunto de filas que está devolviendo de DQ es mucho más pequeño que CJ (como yo esperaría que fuera), entonces el Hash Match escaneará mucho más CJ de lo que necesita
Nota: Utilicé +1 en lugar de +0 porque es probable que el optimizador de consultas reconozca que +0 no cambia nada. Por supuesto, lo mismo podría aplicarse al +1, si no ahora, en algún momento en el futuro.
fuente
Agregar
OPTION (QUERYTRACEON 4138)
desactiva el efecto de los objetivos de fila solo para esa consulta, sin ser demasiado prescriptivo sobre el plan final, y probablemente será la forma más simple / directa.Si agregar esta sugerencia le da un error de permisos (requerido para
DBCC TRACEON
), puede aplicarlo usando una guía del plan:Uso
QUERYTRACEON
en guías de plan por spaghettidba... o simplemente use un procedimiento almacenado:
¿Qué permisos
QUERYTRACEON
necesita? por Kendra Littlefuente
Las versiones más recientes de SQL Server ofrecen opciones diferentes (y posiblemente mejores) para tratar consultas que obtienen un rendimiento subóptimo cuando el optimizador puede aplicar optimizaciones de objetivos de fila. SQL Server 2016 SP1 introdujo el
DISABLE_OPTIMIZER_ROWGOAL USE HINT
que tiene el mismo efecto que el indicador de seguimiento 4138. Si no está en esa versión, también puede considerar usar laOPTIMIZE FOR
sugerencia de consulta para obtener un plan de consulta diseñado para devolver todas las filas en lugar de solo 1. La consulta a continuación devolverá los mismos resultados que el de la pregunta, pero no se creará con el objetivo de obtener solo 1 fila.fuente
Como estás haciendo un
TOP(1)
, te recomiendo hacer elORDER BY
determinista para empezar. Como mínimo, esto garantizará que los resultados sean funcionalmente predecibles (siempre útiles para las pruebas de regresión). Parece que necesitas agregarDC.D_ID
yCJ.CORRESPONDENCE_ID
para eso.Cuando miro los planes de consulta, a veces me parece instructivo simplificar la consulta: posiblemente seleccione todas las filas de CC relevantes en una tabla temporal de antemano, para eliminar problemas con la estimación de cardinalidad
QUEUE_DATE
yPRINT_LOCATION
. Esto debería ser rápido dado el bajo recuento de filas. A continuación, puede agregar índices a esta tabla temporal si es necesario sin alterar la tabla permanente.fuente