Cada vez que me encuentro con este tipo de consultas, siempre me pregunto cómo funcionaría SQL Server. Si ejecuto cualquier tipo de consulta que requiera un cálculo y luego uso ese valor en varios lugares, por ejemplo, en select
y order by
, ¿SQL Server lo calculará dos veces para cada fila o se almacenará en caché? Además, ¿cómo funciona esto con las funciones definidas por el usuario?
Ejemplos:
SELECT CompanyId, Count(*)
FROM Sales
ORDER BY Count(*) desc
SELECT Geom.BufferWithTolerance(@radius, 0.01, 0).STEnvelope().STPointN(1).STX, Geom.BufferWithTolerance(@radius, 0.01, 0).STEnvelope().STPointN(1).STY
FROM Table
SELECT Id, udf.MyFunction(Id)
FROM Table
ORDER BY udf.MyFunction(Id)
¿Hay alguna manera de hacerlo más eficiente o SQL Server es lo suficientemente inteligente como para manejarlo por mí?
sql-server
Jonas Stawski
fuente
fuente
SELECT RAND() FROM Sales order by RAND()
: esto solo se evalúa una vez, ya que es no determinista y una constante de tiempo de ejecución.Respuestas:
El optimizador de consultas de SQL Server puede combinar valores calculados repetidos en un único operador Compute Scalar. Si lo hará o no depende del costo del plan de consulta y de las propiedades del valor calculado. Como se esperaba, no hará esto para valores calculados que no sean deterministas, que son algunas excepciones como
RAND()
. Tampoco lo hará para las funciones definidas por el usuario.Comenzaré con un ejemplo de función definida por el usuario. Aquí hay un excelente ejemplo de una función definida por el usuario:
También quiero crear una tabla y poner 100 filas en ella:
La
dbo.NULL_FUNCTION
función es determinista. ¿Cuántas veces se ejecutará para la siguiente consulta?Según el plan de consulta, esto se ejecutará una vez para cada fila, o 100 veces:
SQL Server 2016 introdujo el DMV sys.dm_exec_function_stats . Podemos tomar instantáneas de ese DMV para ver cuántas veces una consulta ejecuta un UDF.
El resultado de eso es 100, por lo que la función se ejecutó 100 veces.
Probemos con otra consulta simple:
El plan de consulta sugiere que la función se ejecutará 200 veces:
Los resultados de
sys.dm_exec_function_stats
sugieren que la función se ejecutó 200 veces.Tenga en cuenta que no siempre puede usar el plan de consulta para calcular cuántas veces se ejecuta un escalar de cálculo. La siguiente cita es de " Calcular escalares, expresiones y rendimiento del plan de ejecución ":
Probemos con otro ejemplo. Para la siguiente consulta, espero que el UDF se calcule una vez:
El plan de consulta sugiere que se calculará una vez:
Sin embargo, el DMV revela la verdad. El escalar de cálculo se difiere hasta que se necesita, que está en el operador de unión. Se evalúa 100 veces.
También preguntó qué puede hacer para alentar al optimizador a evitar volver a calcular la misma expresión varias veces. Lo mejor que puede hacer es evitar el uso de UDF escalares en su código. Esos tienen una serie de problemas de rendimiento fuera de esta pregunta, que incluyen inflar las concesiones de memoria, obligar a que se ejecute toda la consulta
MAXDOP 1
, estimaciones de cardinalidad incorrecta y conducir a una utilización adicional de la CPU. Si necesita usar un UDF y el valor de ese UDF es una constante, puede calcularlo fuera de la consulta y colocarlo en una variable local.Para consultas sin UDF, puede intentar evitar escribir expresiones que devuelvan el mismo resultado pero que no se escriban exactamente de la misma manera. Para este próximo ejemplo, estoy usando la base de datos AdventureworksDW2016CTP3 disponible públicamente, pero realmente cualquier base de datos funcionará. ¿Cuántas veces se
COUNT(*)
calculará para esta consulta?Para esta consulta, podemos resolver esto mirando el operador Hash Match (agregado).
El
COUNT(*)
se calcula una vez para cada valor único deOrderDateKey
. La inclusión de laORDER BY
cláusula no hace que se calcule dos veces. Puedes ver el plan de ejecución aquí .Ahora, considere una consulta que devolverá exactamente los mismos resultados pero que está escrita de manera diferente:
El optimizador de consultas no es lo suficientemente inteligente como para combinarlos, por lo que se realizará un trabajo adicional:
fuente