SQL Server оценивает функции один раз для каждой строки?

9

У меня есть такой запрос:

SELECT col1
FROM   MyTable
WHERE  
    DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE())) 
       BETWEEN col2 
       AND     col3
;

Это дает всплывающую подсказку о плане выполнения, подобную этой:

Всплывающая подсказка

Выполняется ли dateaddчасть предикатов поиска для каждой строки в запросе? Или SQL Server вычисляет значение один раз для всего запроса?

Стюарт Блэклер
источник

Ответы:

13

Некоторые функции, которые, как известно, являются константами времени выполнения, проходят через процесс, называемый постоянным свертыванием . «Сгибая» константу, выражение вычисляется в начале выполнения запроса, результат кэшируется и вместо этого кэшируется результат, когда это необходимо. Выражение в вашем запросе DATEADD(dd, 0, DATEDIFF(dd, 0, getdate())), afaik, является константой времени выполнения и поэтому будет сгибаться и оцениваться только один раз за запрос.

Как пустяки: RAND()функция, которую можно было бы ожидать, является складной, что приводит к неожиданному поведению. Но другие, например NEWID(), не являются складными и вынуждают оценку для строки.

Ремус Русану
источник
2
@StuartBlackler - Вот демонстрация того, как SQL Server сворачивает функции GETDATE().
Ник Чаммас,
2

Планы выполнения хороши, но иногда они просто не говорят вам правду. Итак, вот доказательство, основанное на тесте производительности.

(и нижняя строка - выражение не оценивается для каждой строки)


;with t(i) as (select 0 union all select i+1 from t where i < 9)
select getdate()-1 as col1,getdate() as col2,getdate() as col3 
into #t 
from t t0,t t1,t t2,t t3,t t4,t t5,t t6,t t7

(Затронуто 100000000 строк)

Это запрос OP, выполнение которого занимает около 12 секунд.

SELECT col1
FROM   #t
WHERE  
    DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE())) 
       BETWEEN col2 
       AND     col3
;

Этот запрос, который сохраняет дату в параметре перед выполнением, занимает примерно 12 секунд.

declare @dt datetime = DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE())) 

SELECT col1
FROM   #t
WHERE  
      @dt
       BETWEEN col2 
       AND     col3
;

И просто для проверки результатов -
этот запрос, который выполняет вычисления на col1 и, следовательно, должен пересчитать выражение для каждой строки, занимает около 30 секунд для выполнения.

SELECT col1
FROM   #t
WHERE  
    DATEADD(dd, 0, DATEDIFF(dd, 0, col1)) 
       BETWEEN col2 
       AND     col3
;

Все запросы выполнялись неоднократно, показывая примерно одинаковые показатели

Дэвид Markודו Марковиц
источник