2010-11-05 7 views
19

que estaba teniendo un tiempo horrible hoy tratando de obtener una consulta para realizar la forma en que se puede esperar. Tuve que hacer un pequeño cambio en una función valorada en la tabla que vive en la consulta de ayer y ese cambio creó un gran impacto en el rendimiento de la consulta. Después de evaluar el plan de ejecución y observar las estadísticas IO y Time, descubrí que, debido a que cambié la función para devolver una variable de tabla en lugar de solo un conjunto de resultados, estaba realizando un análisis completo en una de las tablas consultadas.Tabla función de valor matando a mi rendimiento de las consultas

Mi pregunta es ¿por qué tener que devolver la tabla (TableVariable) en lugar de sólo un Seleccionar/conjunto de resultados causa un cambio tan grande en el plan?

Stumped ....

Respuesta

44

Volviendo una variable de tabla hará que sea una mesa de función con valores instrucción múltiple y puede ser perjudicial para el rendimiento debido al hecho de que es tratado como una mesa, excepto que no hay estadísticas disponible para que SQL Server base un buen plan de ejecución, por lo que estimará que la función devolverá un número muy pequeño de filas. Si devuelve una mayor cantidad de filas, entonces el plan generado podría ser mucho menos que óptimo.

Considerando que devolver solo un SELECCIONAR hace que sea una función de tabla en línea valorada - piénselo más como una vista. En este caso, las tablas subyacentes reales se incluyen en la consulta principal y se puede generar un mejor plan de ejecución basado en las estadísticas adecuadas. Notarás que en este caso, el plan de ejecución NO tendrá mención alguna de la función, ya que básicamente ha combinado la función en la consulta principal.

Hay una gran referencia en ella en MSDN por CSS Ingenieros de SQL Server incluyendo (cita):

Pero si utiliza TVF instrucción múltiple, se trata como al igual que otra mesa . Porque no hay estadísticas disponibles , SQL Server tiene que hacer algunas suposiciones y en general, la posibilidad estimación baja. Si su TVF devuelve solo unas pocas filas, estará bien . Pero si tiene la intención de poblar el TVF con miles de filas y si este TVF se une con otras tablas, el plan ineficiente puede resultado de una estimación de cardinalidad baja.

+2

Agregado una cita del artículo de CSS de referencia que explica mejor que yo :) Tengo que estar en desacuerdo con el voto en negativo. – AdaTheDev

+0

Vi a Kimberly Tripp hacer una demostración sobre esto en una conferencia una vez, con un UDF muy simple que no incluía ninguna tabla, (por lo que las estadísticas serían irelevantes). Todo lo que hizo udf fue convertir un parámetro de entrada usando una expresión simple basada en otra columna en cada fila. Un udf era el valor de la tabla estándar udf, el otro estaba en línea udf. SQL generó un par de millones de filas (una para cada fila en la tabla). Udf en línea procesada en menos de un segundo. El udf estándar tomó varios minutos. –

+3

@charles Bretana Sí, pero está hablando de pasar de UDF escalar en línea a UDF con valor de tabla. Mientras que OP ha pasado de la función de valor de la tabla en línea a la UDF con valor de tabla de multi-estado.Entonces mi respuesta es correcta en este escenario/contexto y no se justifica un voto a la baja – AdaTheDev

3

Realmente imposible responder definitivamente sin más información. Sin embargo, dado que me gusta tomar puñaladas locas en la oscuridad. . .

variables de tabla no pueden ser optimizados por el motor - el motor siempre "asume" que la variable de tabla sólo tiene una fila en ella cuando se genera un plan de ejecución. Esa es una de las razones por las que podrías estar viendo un rendimiento extraño.

4

Esto se debe a que una UDF con múltiples sentencias no se puede procesar en línea con el resto del SQL statememnt en el que se utiliza y, por lo tanto, no puede formar parte del plan de caché de sentencias. Esto significa que debe compilarse por separado del resto del SQL se utiliza en una y otra, para cada fila del conjunto de resultados finales generado por la consulta.

Un Inline valores de tabla UDF, Otoh, se procesa y compilado junto con el SQL que se utiliza en, y por lo tanto se convierte en parte del plan de caché y sólo se procesa y compilado vez, sin importar cuántas filas generes.

+0

¿No es más la diferencia entre una UDF con varios valores de tabla y una UDF con valores de tabla en línea más parecida a la diferencia entre una tabla y una tabla? – binki

+0

No, no lo es. Es lo que dice mi respuesta. ¿Y la diferencia entre una tabla variable y una tabla? ¿O te refieres a la diferencia entre el conjunto de resultados creado por una selección y el conjunto de resultados al que apunta una variable de tabla? porque, una vez creada, no hay diferencia entre esos. Pero hay una enorme diferencia entre una variable de T-SQL (lo que sea que señale y un conjunto de resultados, (sin importar qué se usó para generarlo y construirlo) –

+1

Su respuesta afirma que el problema es el almacenamiento en caché de los planes de consulta. la UDF con valores de tabla multi-declaración está en la cláusula 'FROM' o' JOIN' de la consulta, solo se ejecuta una vez y, si las UDF realmente no tienen sus planes de consulta en caché, eso es solo una compilación en lugar de una compilación Sin embargo, su descripción de cómo funciona la tabla en línea UDF valorada parece correcta. Estoy hablando de cómo SQL Server maneja una tabla simple variable no indexada y la tabla de manera diferente cuando se enumeran en las cláusulas 'FROM' o' JOIN'. – binki

0

Al usar UDF con valores de tabla de varias declaraciones, esa UDF se ejecuta hasta su finalización antes de que la persona que llama pueda utilizar sus resultados. Con una UDF en línea con valores de tabla, SQL Server básicamente expande la UDF en la consulta de llamadas al igual que macro expansion. Esto tiene las siguientes consecuencias, entre otros:

  • cláusula de la consulta de llamadas WHERE se puede interpolar directamente en una UDF con valores de tabla en línea, pero no una UDF instrucción múltiple. Por lo tanto, si su UDF con valores de tabla genera muchas filas que serían filtradas por la cláusula WHERE de la consulta, el optimizador de consultas puede aplicar la cláusula WHERE directamente en una UDF con valores de tabla en línea pero no en una UDF de instrucciones múltiples .
  • Un UDF con valores de tabla en línea se comporta como un VIEW parametrizado si SQL Server tuviera tal concepto, mientras que un UDF con valores de tabla multi-instrucción se comportaría como usted lo hizo y luego usó una variable de tabla en su consulta.

Si su UDF devuelve muchas filas y está respaldada por una tabla, me imagino que este podría ser el origen del escaneo de la tabla. Agregue más parámetros a su UDF para permitir que la persona que llama restrinja su tamaño de resultado o intente reformularlo como una UDF con valores de tabla en línea con la ayuda de amigos como UNION y otros. Evitaría las UDF con valores de tabla multi-declaración a toda costa, a menos que se conozca que el tamaño del resultado es de pocas filas y, es difícil producir los resultados requeridos con la lógica basada en conjuntos.

0

En SQL Server 2014 pudimos resolver nuestro problema insertando datos de funciones de valores de tabla en tablas temporales y luego unirnos a ellos. En lugar de hacer una unión directamente a la función de valor de la tabla.

Esto mejoró nuestro tiempo de ejecución de 2 minutos a 4 segundos.

Aquí es un ejemplo que trabajó para nuestro equipo:

--SLOW CONSULTA (2 min):

DECLARE @id INT = 1; 

SELECT * 
FROM [data].[someTable] T 
INNER JOIN [data].[tableValueFunction](@id) TVF ON TVF.id = T.id; 

--fast consulta (4 seg):

DECLARE @id INT = 1; 

SELECT * 
INTO #tableValueFunction 
FROM [data].[tableValueFunction](@id) TVF 

SELECT * 
FROM [data].[someTable] T 
INNER JOIN #tableValueFunction TVF ON TVF.id = T.id; 
Cuestiones relacionadas