Возможно ли это условие сравнения дат SARG в SQL?

Возможно ли это условие?

AND  DATEDIFF(month,p.PlayerStatusLastTransitionDate,@now) BETWEEN 1 AND 7)

Мое эмпирическое правило состоит в том, что функция слева делает условие несаргируемым ... но в некоторых местах я читал, что предложение BETWEEN является саргируемым. Так кто-нибудь знает наверняка?

Для справки:

ПРИМЕЧАНИЕ. Если на этом заканчивается какой-либо гуру, обновите страницу Sargable в Википедии. Я немного обновил его, но уверен, что его можно улучшить еще :)


person Oscar Foley    schedule 01.06.2012    source источник
comment
BETWEEN - это просто сокращение для ›= AND‹ =. Почему в данном случае это повлияет на саргентируемость?   -  person Aaron Bertrand    schedule 01.06.2012
comment
Просто подумайте, что функция слева (РАЗНДАТ) может повлиять на саргабильность (вау! Что за слово!) ...   -  person Oscar Foley    schedule 01.06.2012
comment
Также см. sqlperformance.com/2013/09/t-sql-queries / dateiff-bug   -  person Aaron Bertrand    schedule 18.09.2014


Ответы (2)


Используя AdventureWorks, если мы посмотрим на эти два эквивалентных запроса:

SELECT OrderDate FROM Sales.SalesOrderHeader
WHERE DATEDIFF(month,OrderDate,GETDATE()) BETWEEN 1 AND 7;

SELECT OrderDate FROM Sales.SalesOrderHeader
WHERE OrderDate >= DATEADD(MONTH, -7, GETDATE())
  AND OrderDate <= DATEADD(MONTH, -1, GETDATE());

В обоих случаях мы видим сканирование кластерного индекса:

введите описание изображения здесь

Но обратите внимание на рекомендуемый / отсутствующий индекс только в последнем запросе, так как он единственный, кому он может быть полезен:

введите описание изображения здесь

Если мы добавим индекс в столбец OrderDate, то снова запустим запросы:

CREATE INDEX dt ON Sales.SalesOrderHeader(OrderDate);
GO

SELECT OrderDate FROM Sales.SalesOrderHeader
WHERE DATEDIFF(month,OrderDate,GETDATE()) BETWEEN 1 AND 7;

SELECT OrderDate FROM Sales.SalesOrderHeader
WHERE OrderDate >= DATEADD(MONTH, -7, GETDATE())
  AND OrderDate <= DATEADD(MONTH, -1, GETDATE());

Мы видим большую разницу - последний использует поиск:

введите описание изображения здесь

введите описание изображения здесь

Также обратите внимание на то, что оценки для вашей версии запроса сильно отличаются. Это может иметь катастрофические последствия для большого набора данных.

В очень редких случаях функция или другое выражение, примененное к столбцу, будет саргируемым. Один случай, о котором я знаю, - это CONVERT(DATE, datetime_column), но эта конкретная оптимизация недокументирована, и я все равно рекомендую держаться подальше от нее. Не только потому, что вы неявно предполагаете, что использование функций / выражений для столбцов - это нормально (это не во всех других сценариях), но и потому, что это может привести к потере чтения и катастрофическим оценкам.

person Aaron Bertrand    schedule 01.06.2012

Я был бы очень удивлен, если бы это было возможно. Один из вариантов может заключаться в том, чтобы переписать его как:

WHERE p.PlayerStatusLastTransitionDate >= DATEADD(month,1,CAST(@now AS DATE))
AND   p.PlayerStatusLastTransitionDate <= DATEADD(month,7,CAST(@now AS DATE))

Что, я считаю, будет привлекательным (хотя это не так красиво).

person Abe Miessler    schedule 01.06.2012
comment
+1: запрос OPs не подлежит поиску. table.field BETWEEN f(x) AND f(y) (или с использованием >= и <=, как это сделал Абэ) подлежит поиску по запросу. В значительной степени, если вы поместите поле поиска в функцию, оно не будет доступно для поиска. Вместо этого обработайте параметры для поиска, как ответ Абэ :) - person MatBailie; 01.06.2012
comment
Этот ответ можно перефразировать с помощью BETWEEN. Важно то, что функция даты применяется к выражениям, которые являются постоянными для выполнения запроса, а не к каждой строке, как в запросе OP. - person HABO; 01.06.2012
comment
Я не рекомендую использовать BETWEEN для запросов datetime, в вашем собственном коде или для кода, который вы предоставляете другим. Вот почему: sqlblog.com/blogs/aaron_bertrand/archive/2011/10/19/ - person Aaron Bertrand; 01.06.2012