sql >> Base de Datos >  >> RDS >> Sqlserver

Rendimiento de las variables de tabla en SQL Server

En este artículo, vamos a tocar el tema del rendimiento de las variables de la tabla. En SQL Server, podemos crear variables que operarán como tablas completas. Tal vez, otras bases de datos tengan las mismas capacidades, sin embargo, utilicé tales variables solo en MS SQL Server.

Así, puedes escribir lo siguiente:

declare @t as table (int value)

Aquí, declaramos la variable @t como una tabla que contendrá una única columna de valor del tipo entero. Es posible crear tablas más complejas, sin embargo, en nuestro ejemplo, una columna es suficiente para explorar la optimización.

Ahora, podemos usar esta variable en nuestras consultas. Podemos agregarle muchos datos y realizar la recuperación de datos de esta variable:

insert into @t
select UserID
from User
or
select * from @t

Noté que las variables de la tabla se usan cuando es necesario obtener datos para una gran selección. Por ejemplo, hay una consulta en el código que devuelve usuarios del sitio. Ahora, recopila las ID de todos los usuarios, las agrega a la variable de la tabla y puede buscar las direcciones de estos usuarios. Tal vez, alguien puede preguntar por qué no ejecutamos una consulta en la base de datos y obtenemos todo de inmediato. Tengo un ejemplo simple.

Suponga que los usuarios provienen del servicio web, mientras que sus direcciones se almacenan en su base de datos. En este caso, no hay salida. Obtuvimos un montón de ID de usuario del servicio y, para evitar consultar la base de datos, alguien decide que es más fácil agregar todas las ID al parámetro de consulta como una variable de tabla y la consulta se verá ordenada:

select *
from @t as users 
   join Address a on a.UserID = users.UserID
os

Todo esto funciona correctamente. En el código C#, puede combinar rápidamente los resultados de ambas matrices de datos en un objeto mediante LINQ. Sin embargo, el rendimiento de la consulta puede verse afectado.

El hecho es que las variables de la tabla no fueron diseñadas para procesar grandes volúmenes de datos. Si no me equivoco, el optimizador de consultas siempre utilizará el método de ejecución LOOP. Por lo tanto, para cada ID de @t, se realizará una búsqueda en la tabla de direcciones. Si hay 1000 registros en @t, el servidor escaneará la dirección 1000 veces.

En términos de ejecución, debido a la increíble cantidad de escaneos, el servidor simplemente deja de intentar encontrar datos.

Es mucho más efectivo escanear toda la tabla de direcciones y encontrar a todos los usuarios a la vez. Este método se llama MERGE. Sin embargo, SQL Server lo elige cuando hay muchos datos ordenados. En este caso, el optimizador no sabe cuánto y qué datos se agregarán a la variable, y si hay clasificación porque dicha variable no incluye índices.

Si hay pocos datos en la variable de la tabla y no inserta miles de filas, todo está bien. Sin embargo, si le gusta usar tales variables y agregarles una gran cantidad de datos, debe continuar leyendo.

Incluso si reemplaza la variable de la tabla con SQL, acelerará en gran medida el rendimiento de las consultas:

select *
from (
 Select 10377 as UserID
 Union all
 Select 73736
 Union all
 Select 7474748
 ….
  ) as users 
   join Address a on a.UserID = users.UserID

Puede haber mil de tales declaraciones SELECT y el texto de la consulta será enorme, pero se ejecutará miles de veces más rápido para una gran cantidad de datos porque SQL Server puede elegir un plan de ejecución efectivo.

Esta consulta no se ve muy bien. Sin embargo, su plan de ejecución no se puede almacenar en caché porque cambiar solo una ID también cambiará todo el texto de la consulta y no se pueden usar los parámetros.

Creo que Microsoft no esperaba que los usuarios usaran variables tabulares de esta manera, pero existe una buena solución.

Hay varias formas de resolver este problema. Sin embargo, en mi opinión, lo más efectivo en términos de rendimiento es agregar OPCIÓN (RECOMPILAR) al final de la consulta:

select *
from @t as users 
   join Address a on a.UserID = users.UserID
OPTION (RECOMPILE)

Esta opción se agrega una vez al final de la consulta, incluso después de ORDER BY. El propósito de esta opción es hacer que SQL Server vuelva a compilar la consulta en cada ejecución.

Si medimos el rendimiento de la consulta después de eso, lo más probable es que se reduzca el tiempo para realizar la búsqueda. Con grandes datos, la mejora del rendimiento puede ser significativa, desde decenas de minutos hasta segundos. Ahora, el servidor compila su código antes de ejecutar cada consulta y no usa el plan de ejecución del caché, sino que genera uno nuevo, dependiendo de la cantidad de datos en la variable, y esto suele ayudar mucho.

El inconveniente es que el plan de ejecución no se almacena y el servidor tiene que compilar la consulta y buscar un plan de ejecución efectivo cada vez. Sin embargo, no he visto las consultas en las que este proceso tomó más de 100 ms.

¿Es una mala idea usar variables de tabla? No, no es. Solo recuerda que no fueron creados para grandes datos. A veces, es mejor crear una tabla temporal, si hay muchos datos, e insertar datos en esta tabla, o incluso crear un índice sobre la marcha. Tuve que hacer esto con los informes, aunque sólo una vez. En ese entonces, reduje el tiempo para generar un informe de 3 horas a 20 minutos.

Prefiero usar una consulta grande en lugar de dividirla en varias consultas y almacenar los resultados en variables. Permita que SQL Server ajuste el rendimiento de una consulta grande y no lo defraudará. Tenga en cuenta que debe recurrir a las variables de tabla solo en casos extremos cuando realmente vea sus beneficios.