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 where
clá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 scan
en todas las tablas ignorando los índices. Devuelve 5 millones de filas.- Enorme unión.
- Cálculo de ventanas sobre todos los
partition
s (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?
order_number
No es una clave primaria. Estáint not null
con índice no agrupado en ambas tablas.OPTION (RECOMPILE)
ayuda?Respuestas:
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
recompile
sugerencia sería reescribir la consulta para usar un TVF en línea parametrizado como lo sugiere @ a1ex07)fuente
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_number
en élfuente
SELECT * FROM my_funct(12345)
it will filter records first, and then apply window function
Es incorrecto. No hay una orden determinista de ejecución