Sempre que me deparo com esse tipo de consulta, sempre me pergunto como o SQL Server funcionaria. Se eu executar qualquer tipo de consulta que exija um cálculo e, em seguida, usar esse valor em vários locais, por exemplo, no select
e no order by
, o SQL Server o calculará duas vezes para cada linha ou será armazenado em cache? Além disso, como isso funciona com as funções definidas pelo usuário?
Exemplos:
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)
Existe uma maneira de torná-lo mais eficiente ou o SQL Server é inteligente o suficiente para lidar com isso para mim?
sql-server
Jonas Stawski
fonte
fonte
SELECT RAND() FROM Sales order by RAND()
- isso é avaliado apenas uma vez, pois é não determinístico e constante de tempo de execução.Respostas:
O otimizador de consulta do SQL Server pode combinar valores calculados repetidos em um único operador do Compute Scalar. A decisão de fazer ou não isso depende do custo do plano de consulta e das propriedades do valor calculado. Como esperado, ele não fará isso para valores calculados que são não determinísticos, e algumas exceções, como
RAND()
. Também não fará isso para funções definidas pelo usuário.Começarei com um exemplo de função definida pelo usuário. Aqui está um excelente exemplo de uma função definida pelo usuário:
Também quero criar uma tabela e colocar 100 linhas nela:
A
dbo.NULL_FUNCTION
função é determinista. Quantas vezes será executado para a seguinte consulta?Com base no plano de consulta, isso será executado uma vez para cada linha ou 100 vezes:
O SQL Server 2016 apresentou a DMV sys.dm_exec_function_stats . Podemos tirar instantâneos dessa DMV para ver quantas vezes um UDF é executado por uma consulta.
O resultado é 100, então a função foi executada 100 vezes.
Vamos tentar outra consulta simples:
O plano de consulta sugere que a função seja executada 200 vezes:
Os resultados
sys.dm_exec_function_stats
sugerem que a função foi executada 200 vezes.Observe que você nem sempre pode usar o plano de consulta para descobrir quantas vezes um escalar de computação é executado. A citação a seguir é de " Escalares de computação, expressões e desempenho do plano de execução ":
Vamos tentar outro exemplo. Para a seguinte consulta, espero que o UDF seja calculado uma vez:
O plano de consulta sugere que será calculado uma vez:
No entanto, o DMV revela a verdade. O escalar de computação é adiado até que seja necessário, que está no operador de junção. É avaliado 100 vezes.
Você também perguntou o que pode fazer para incentivar o otimizador a evitar recalcular a mesma expressão várias vezes. A melhor coisa que você pode fazer é evitar o uso de UDFs escalares no seu código. Eles têm vários problemas de desempenho fora dessa questão, incluindo inflar concessões de memória, forçando a execução de toda a consulta
MAXDOP 1
, estimativas ruins de cardinalidade e levando a utilização adicional da CPU. Se você precisar usar um UDF e o valor desse UDF for uma constante, poderá calculá-lo fora da consulta e colocá-lo em uma variável local.Para consultas sem UDFs, você pode evitar escrever expressões que retornam o mesmo resultado, mas não são digitadas exatamente da mesma maneira. Neste próximo exemplo, estou usando o banco de dados AdventureworksDW2016CTP3 disponível publicamente, mas qualquer banco de dados serve. Quantas vezes serão
COUNT(*)
calculadas para esta consulta?Para esta consulta, podemos descobrir isso observando o operador Hash Match (agregate).
O
COUNT(*)
é calculado uma vez para cada valor exclusivo deOrderDateKey
. A inclusão daORDER BY
cláusula não faz com que seja calculada duas vezes. Você pode ver o plano de execução aqui .Agora, considere uma consulta que retornará exatamente os mesmos resultados, mas que seja escrita de uma maneira diferente:
O otimizador de consulta não é inteligente o suficiente para combiná-los; portanto, trabalho adicional será feito:
fonte