Каждый раз, когда я сталкиваюсь с запросами такого типа, мне всегда интересно, как SQL Server справится с этим. Если я выполню запрос любого типа, требующий вычисления, а затем использую это значение в нескольких местах, например в select
и order by
, SQL Server будет рассчитывать его дважды для каждой строки или он будет кэширован? Кроме того, как это работает с пользовательскими функциями?
Примеры:
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)
Есть ли способ сделать его более эффективным или SQL Server достаточно умен, чтобы справиться с этим для меня?
sql-server
Йонас Ставски
источник
источник
SELECT RAND() FROM Sales order by RAND()
- это оценивается только один раз, так как оно является как недетерминированным, так и постоянной времени выполнения.Ответы:
Оптимизатор запросов SQL Server может объединять повторяющиеся вычисленные значения в один оператор Compute Scalar. Будет ли это сделано, зависит от стоимости плана запроса и свойств рассчитанного значения. Как и ожидалось, он не будет делать это для вычисленных значений, которые являются недетерминированными, что, за исключением нескольких, таких как
RAND()
. Это также не будет делать это для пользовательских функций.Я начну с примера пользовательской функции. Вот отличный пример пользовательской функции:
Я также хочу создать таблицу и поместить в нее 100 строк:
dbo.NULL_FUNCTION
Функция determistic. Сколько раз он будет выполнен для следующего запроса?На основании плана запроса это будет выполнено один раз для каждой строки или 100 раз:
SQL Server 2016 представил DMV sys.dm_exec_function_stats . Мы можем сделать снимки этого DMV, чтобы увидеть, сколько раз UDF выполняется запросом.
Результат равен 100, поэтому функция была выполнена 100 раз.
Давайте попробуем еще один простой запрос:
План запроса предполагает, что функция будет выполнена 200 раз:
Результаты
sys.dm_exec_function_stats
свидетельствуют о том, что функция была выполнена 200 раз.Обратите внимание, что вы не всегда можете использовать план запроса, чтобы выяснить, сколько раз выполняется вычислительный скаляр. Следующая цитата из " Вычислить скаляры, выражения и производительность плана выполнения ":
Давайте попробуем другой пример. Для следующего запроса я надеюсь, что UDF рассчитывается один раз:
План запроса предполагает, что он будет рассчитан один раз:
Тем не менее, DMV раскрывает правду. Вычислительный скаляр откладывается до тех пор, пока он не понадобится, что находится в операторе соединения. Это оценивается 100 раз.
Вы также спросили, что вы можете сделать, чтобы оптимизатор избегал повторного вычисления одного и того же выражения несколько раз. Лучшее, что вы можете сделать, это избегать использования скалярных UDF в вашем коде. У них есть ряд проблем с производительностью, выходящих за рамки этого вопроса, в том числе раздувание выделений памяти, принудительное выполнение всего запроса
MAXDOP 1
, неверные оценки мощности и ведение к дополнительной загрузке ЦП. Если вам нужно использовать UDF и значение этого UDF является константой, вы можете вычислить его вне запроса и поместить в локальную переменную.Для запросов без UDF вы можете попытаться избежать написания выражений, которые возвращают один и тот же результат, но не набираются одинаково. Для этого следующего примера я использую общедоступную базу данных AdventureworksDW2016CTP3, но на самом деле подойдет любая база данных. Сколько раз будет
COUNT(*)
рассчитано для этого запроса?Для этого запроса мы можем выяснить это, посмотрев на оператор Hash Match (aggregate).
COUNT(*)
Вычисляется один раз для каждого уникального значенияOrderDateKey
. ВключениеORDER BY
пункта не приводит к тому, что он рассчитывается дважды. Вы можете увидеть план выполнения здесь .Теперь рассмотрим запрос, который вернет точно такие же результаты, но написан по-другому:
Оптимизатор запросов недостаточно умен, чтобы объединять их, поэтому будет проделана дополнительная работа:
источник