¿SQL Server almacena en caché el resultado de una función con valores de tabla de varias instrucciones?


Respuestas:


23

Los resultados de una función con valores de tabla de varias instrucciones (msTVF) nunca se almacenan en caché o se reutilizan en las instrucciones (o conexiones), pero hay un par de formas en que un resultado de msTVF se puede reutilizar dentro de la misma instrucción. En ese sentido, un msTVF no se repobla necesariamente cada vez que se llama.

Ejemplo msTVF

Este (deliberadamente ineficiente) msTVF devuelve un rango específico de enteros, con una marca de tiempo en cada fila:

IF OBJECT_ID(N'dbo.IntegerRange', 'TF') IS NOT NULL
    DROP FUNCTION dbo.IntegerRange;
GO
CREATE FUNCTION dbo.IntegerRange (@From integer, @To integer)
RETURNS @T table 
(
    n integer PRIMARY KEY, 
    ts datetime DEFAULT CURRENT_TIMESTAMP
)
WITH SCHEMABINDING
AS
BEGIN
    WHILE @From <= @To
    BEGIN
        INSERT @T (n)
        VALUES (@From);

        SET @From = @From + 1;
    END;
    RETURN;
END;

Tabla estática variable

Si todos los parámetros para la llamada a la función son constantes (o constantes de tiempo de ejecución), el plan de ejecución rellenará el resultado de la variable de tabla una vez. El resto del plan puede acceder a la variable de tabla muchas veces. La naturaleza estática de la variable de tabla puede reconocerse a partir del plan de ejecución. Por ejemplo:

SELECT
    IR.n,
    IR.ts 
FROM dbo.IntegerRange(1, 5) AS IR
ORDER BY
    IR.n;

Devuelve un resultado similar a:

Resultado simple

El plan de ejecución es:

Plan de ejecución simple

El operador de secuencia primero llama al operador de función de valor de tabla, que llena la variable de tabla (tenga en cuenta que este operador no devuelve filas). A continuación, la secuencia llama a su segunda entrada, que devuelve el contenido de la variable de la tabla (en este caso, utilizando un análisis de índice agrupado).

El obsequio de que el plan está utilizando un resultado de variable de tabla 'estática' es el operador de Función de valor de tabla debajo de una secuencia: la variable de tabla debe completarse una vez antes de que el resto del plan pueda ponerse en marcha.

Múltiples accesos

Para mostrar el resultado de la variable de tabla al que se accede más de una vez, utilizaremos una segunda tabla con filas numeradas del 1 al 5:

IF OBJECT_ID(N'dbo.T', 'U') IS NOT NULL
    DROP TABLE dbo.T;

CREATE TABLE dbo.T (i integer NOT NULL);

INSERT dbo.T (i) 
VALUES (1), (2), (3), (4), (5);

Y una nueva consulta que une esta tabla a nuestra función (esto también podría escribirse como APPLY):

SELECT T.i,
       IR.n,
       IR.ts
FROM dbo.T AS T
JOIN dbo.IntegerRange(1, 5) AS IR
    ON IR.n = T.i;

El resultado es:

Unir resultado

El plan de ejecución:

Unirse al plan

Como antes, la secuencia llena primero el resultado de msTVF de la variable de tabla. A continuación, los bucles anidados se utilizan para unir cada fila de la tabla Ta una fila del resultado de msTVF. Como la definición de la función incluía un índice útil en la variable de la tabla, se puede utilizar una búsqueda de índice.

El punto clave es que cuando los parámetros para msTVF son constantes (incluidas variables y parámetros) o se tratan como constantes de tiempo de ejecución para la declaración del motor de ejecución, el plan contará con dos operadores separados para el resultado de la variable de tabla msTVF: uno para completar el mesa; otro para acceder a los resultados, posiblemente accediendo a la tabla varias veces, y posiblemente haciendo uso de índices declarados en la definición de la función.

Parámetros correlacionados y parámetros no constantes.

Para resaltar las diferencias cuando se utilizan parámetros correlacionados (referencias externas) o parámetros de función no constantes, cambiaremos el contenido de la tabla Tpara que la función tenga mucho más trabajo por hacer:

TRUNCATE TABLE dbo.T;

INSERT dbo.T (i) 
VALUES (50001), (50002), (50003), (50004), (50005);

La siguiente consulta modificada ahora usa una referencia externa a la tabla Ten uno de los parámetros de función:

SELECT T.i,
       IR.n,
       IR.ts
FROM dbo.T AS T
CROSS APPLY dbo.IntegerRange(1, T.i) AS IR
WHERE IR.n = T.i;

Esta consulta demora alrededor de 8 segundos para devolver resultados como:

Resultado correlacionado

Observe la diferencia horaria entre filas en la columna ts. La WHEREcláusula limita el resultado final para una salida de tamaño razonable, pero la función ineficiente todavía tarda un tiempo en llenar la variable de la tabla con 50,000 filas impares (dependiendo del valor correlacionado de la itabla T).

El plan de ejecución es:

Plan de ejecución correlacionado

Observe la falta de un operador de secuencia. Ahora, hay un único operador de Función de valor de tabla que llena la variable de tabla y devuelve sus filas en cada iteración de la unión de bucles anidados.

Para ser claros: con solo 5 filas en la tabla T, el operador de Función de valor de tabla se ejecuta 5 veces. Genera 50.001 filas en la primera iteración, 50.002 en la segunda ... y así sucesivamente. La variable de la tabla se 'desecha' (trunca) entre iteraciones, por lo que cada una de las cinco llamadas es una población completa. Es por eso que es tan lento y cada fila tarda aproximadamente el mismo tiempo en aparecer en el resultado.

Notas al margen:

Naturalmente, el escenario anterior está ideado deliberadamente para mostrar cuán pobre puede ser el rendimiento cuando el msTVF llena muchas filas en cada iteración.

Una implementación sensata del código anterior establecería ambos parámetros de msTVF iy eliminaría la WHEREcláusula redundante . La variable de tabla todavía se truncaría y se volvería a llenar en cada iteración, pero solo con una fila cada vez.

También podríamos obtener los ivalores mínimos y máximos Ty almacenarlos en variables en un paso anterior. Llamar a la función con variables en lugar de parámetros correlacionados permitiría usar el patrón variable de tabla 'estático' como se indicó anteriormente.

Almacenamiento en caché para parámetros correlacionados sin cambios

Volviendo a abordar la pregunta original una vez más, donde el patrón estático de Secuencia no se puede usar, SQL Server puede evitar truncar y repoblar la variable de tabla msTVF si ninguno de los parámetros correlacionados ha cambiado desde la iteración previa de una unión de bucle anidado.

Para demostrar esto, reemplazaremos el contenido de Tcon cinco valores idénticos i :

TRUNCATE TABLE dbo.T;

INSERT dbo.T (i) 
VALUES (50005), (50005), (50005), (50005), (50005);

La consulta con un parámetro correlacionado nuevamente:

SELECT T.i,
       IR.n,
       IR.ts
FROM dbo.T AS T
CROSS APPLY dbo.IntegerRange(1, T.i) AS IR
WHERE IR.n = T.i;

Esta vez, los resultados aparecen en alrededor de 1,5 segundos :

Resultados de fila idénticos

Tenga en cuenta las marcas de tiempo idénticas en cada fila. El resultado almacenado en caché en la variable de tabla se reutiliza para las iteraciones posteriores en las que el valor correlacionado ino cambia. Reutilizar el resultado es mucho más rápido que insertar 50,005 filas cada vez.

El plan de ejecución es muy similar al anterior:

Plan para filas idénticas

La diferencia clave está en las propiedades Rebobinados reales y Rebobinados reales del operador Función de valor de tabla:

Propiedades del operador

Cuando los parámetros correlacionados no cambian, SQL Server puede reproducir (rebobinar) los resultados actuales en la variable de tabla. Cuando la correlación cambia, SQL Server debe truncar y repoblar la variable de la tabla (volver a vincular). El nuevo enlace ocurre en la primera iteración; Las cuatro iteraciones posteriores son todas rebobinadas ya que el valor de no T.iha cambiado.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.