Las funciones de la ventana provocan un plan de ejecución horrible cuando se llama desde una vista con una cláusula externa 'dónde' parametrizada

10

Tuve este problema hace mucho tiempo, encontré una solución que me convenía y lo olvidé.

Pero ahora está esa pregunta sobre SO, así que estoy dispuesto a plantear este problema.

Hay una vista que une pocas tablas de una manera muy sencilla (pedidos + líneas de pedido).

Cuando se consulta sin una wherecláusula, la vista devuelve varios millones de líneas.
Sin embargo, nadie lo llama así. La consulta habitual es

select * from that_nasty_view where order_number = 123456;

Esto devuelve aproximadamente 10 registros de 5m.

Una cosa importante: la vista contiene una función de ventana rank(), que está dividida exactamente por el campo con el que siempre se consulta la vista:

rank() over (partition by order_number order by detail_line_number)

Ahora, si esta vista se consulta con parámetros literales en la cadena de consulta, exactamente como se muestra arriba, devuelve las filas al instante. El plan de ejecución está bien:

  • Búsqueda de índice en ambas tablas usando los índices en order_number(devuelve 10 filas).
  • Cálculo de ventanas sobre el pequeño resultado devuelto.
  • Seleccionando.

Sin embargo, cuando la vista se llama de forma parametrizada, las cosas se ponen feas:

  • Index scanen todas las tablas ignorando los índices. Devuelve 5 millones de filas.
  • Enorme unión.
  • Cálculo de ventanas sobre todos los partitions (aproximadamente 500k ventanas).
  • Filter tomar 10 filas de 5m.
  • Seleccione

Esto sucede en todos los casos cuando hay parámetros involucrados. Puede ser SSMS:

declare @order_number int = 123456;
select * from that_nasty_view where order_number = @order_number;

Puede ser un cliente ODBC, como Excel:

select * from that_nasty_view where order_number = ?

O puede ser cualquier otro cliente que use parámetros y no concatenación SQL.

Si la función de ventana se elimina de la vista, se ejecuta perfectamente rápido, independientemente de si se consulta o no con parámetros.

Mi solución fue eliminar la función infractora y volver a aplicarla en una etapa posterior.

Pero, ¿qué da? ¿Es realmente un error en cómo SQL Server 2008 maneja las funciones de la ventana?

GSerg
fuente
order_number es la clave principal? ¿Tipos de datos de columna y coincidencia de parámetros?
gbn
order_numberNo es una clave primaria. Está int not nullcon índice no agrupado en ambas tablas.
GSerg
55
SQL Server 2005 tuvo problemas con la inserción de predicados en esta área. Pensé que ya estaban arreglados. Por cierto, su ejemplo de TSQL usa una variable, no un parámetro. ¿Agregar OPTION (RECOMPILE)ayuda?
Martin Smith
1
@GSerg - Entonces, ¿en el mal plan con el filtro pasado tiene un estimado de 5 millones de filas en el filtro y un estimado de 10 filas que coinciden con las reales? Si es así, tal vez sea ese problema de empuje de predicados que aún no se ha solucionado por completo.
Martin Smith

Respuestas:

5

Esto parece ser un problema de larga data que sigue resurgiendo de una forma u otra y todavía está presente en SQL Server 2012.

Algunas publicaciones que lo discuten son

Todas las versiones actuales de SQL Server hasta 2012 inclusive no pueden empujar el filtro en un grupo de particionamiento más allá del proyecto de secuencia para un predicado parametrizado, excepto si option(recompile)se usa (si 2008+).

Una alternativa a la recompilesugerencia sería reescribir la consulta para usar un TVF en línea parametrizado como lo sugiere @ a1ex07)

Martin Smith
fuente
También tuve el caso en SQL Server 2014
Guillaume86
3

Intentaría reemplazar la vista por udf con valores de tabla. De esa manera, primero filtrará los registros y luego aplicará la función de ventana. Esta función puede aceptar parámetro de tabla para que pueda pasar múltiples order_numberen él

a1ex07
fuente
Otra solución alternativa, sí. Sin embargo, no pude hacerlo porque no todos los clientes pudieron consumir una función con valores de tabla.
GSerg
¿Por qué? No estoy 100% seguro, pero creo que todo lo que necesita es cambiar la consulta un poco a algo comoSELECT * FROM my_funct(12345)
a1ex07
Uno de los requisitos era que los usuarios finales pudieran consumir la consulta utilizando Excel (es decir, por MS Query), y MS Query no le permitirá hacerlo, al menos en versiones hasta 2003.
GSerg
it will filter records first, and then apply window functionEs incorrecto. No hay una orden determinista de ejecución
Remus Rusanu,