Я пытаюсь найти наиболее эффективный (лучший по производительности) способ проверить поле даты на текущую дату. В настоящее время мы используем:
SELECT COUNT(Job) AS Jobs
FROM dbo.Job
WHERE (Received BETWEEN DATEADD(d, DATEDIFF(d, 0, GETDATE()), 0)
AND DATEADD(d, DATEDIFF(d, 0, GETDATE()), 1))


это лучший способ сделать это. вы можете поместить DATEADD (d, DATEDIFF (d, 0, GETDATE ()), 0) и DATEADD (d, DATEDIFF (d, 0, GETDATE ()), 1) в переменные и использовать их вместо этого, но я не думаю, что это улучшит производительность.
Если вы просто хотите найти все записи с сегодняшней датой получения и есть записи с будущими датами получения, то то, что вы делаете (очень-очень незначительно), неверно ... Поскольку оператор Between допускает равные значения до конечной границы, чтобы вы могли получать записи с датой получения = до полуночи завтра ...
Если нет необходимости использовать индекс для Received, все, что вам нужно сделать, это проверить, что разница даты с текущим datetime равна 0 ...
Where DateDiff(day, received, getdate()) = 0
Этот предикат, конечно, не SARGable, поэтому он не может использовать индекс ... Если это проблема для этого запроса, то, если у вас не может быть дат получения в будущем, я бы использовал это вместо этого ...
Where Received >= DateAdd(day, DateDiff(Day, 0, getDate()), 0)
Если даты получения могут быть в будущем, то вы, вероятно, настолько близки к наиболее эффективным, насколько это возможно ... (за исключением изменения значения Between на> = AND <)
Чарльз, даже без индекса DateDiff(day, received, getdate()) не лучший вариант, потому что он заставляет вычислять каждую строку в таблице, без всякой причины используя больше ЦП.
@Emtucifor, верно, но по сравнению с чтением дискового ввода-вывода, циклы процессора настолько незначительны, что не имеют значения. Здесь мы говорим о разнице на три-четыре порядка.
Верно, Чарльз. Спасибо, что взглянули на мои придирки в перспективе. :) Я действительно думаю, что лучше рекомендовать последнее, где это возможно, потому что, когда есть индекс, это серьезно повлияет на ввод-вывод.
@Emtucifor, вы снова правы ... и, конечно, лучше всего, когда у вас есть на это право, - это установить соответствующий индекс и разработать запросы для их использования.
WHERE
DateDiff(d, Received, GETDATE()) = 0
Обновлено: как указано в комментариях к этому ответу, это не идеальное решение. Проверьте и другие ответы в этой теме.
Я бы не стал так поступать, потому что это не поддерживает SARG.
@Mitch Wheat: Пока нет столбца только с датой И указателем на нем, ничего из того, что вы можете сделать, все равно будет SARGable.
Tomalak, нет - его первоначальное решение SARGable ... Где было получено> = {Midnight This morning} And Received <{Midnight Tonight} - SARGable
Хорошие моменты о SARGable. Какое тогда решение было бы оптимальным?
если вы собираетесь сделать это таким образом, то нет смысла отдельно вычислять DateDiff ((d, 0, getdate ()) ... Просто выполните single dateiff - вычислите DateDiff beterrn Received и getdate () - должно быть равно нулю для все даты за сегодня Где DateDiff (d, Received, getDate ()) = 0
База данных может не использовать индекс. (как MSSQL 2000/2005)
Что, черт возьми, «поддерживает SARG»?
Это означает, что условие поиска может быть выполнено с помощью индекса (en.wikipedia.org/wiki/Sargable). Вычисление значения, по которому выполняется фильтрация, делает операцию недоступной для SARG, поскольку вычисленные значения не входят в индекс. Это заставляет сервер смотреть на каждую строку отдельно (= медленно).
ага ... Я подумал, что это было что-то вроде этого (я бы википедию написал "SARG", но ничего не нашел)
Этот метод не годится. Даже если в столбце ReceivedDate нет индекса, зачем заставлять ядро базы данных выполнять преобразование для каждой отдельной строки, а не только один раз? Выполните расчет и используйте синтаксис диапазона дат.
Он спрашивает, как лучше всего, а этот не использует индексы. НЕ ИСПОЛЬЗУЙТЕ ЕГО. Период..
... как я сказал в своем ответе, который вы на самом деле не читали, хотя он находится в предложении непосредственно под кодом.
Я не уверен, как вы определяете «лучший», но это сработает.
Однако, если вы собираетесь запускать этот запрос неоднократно, вам следует избавиться от функции get_date () и просто вставить туда буквальное значение даты с помощью любого языка программирования, на котором вы его запускаете. Несмотря на то, что их вывод изменяется только один раз каждые 24 часа get_date (), current_date () и т. д. являются функциями недетерминированный, что означает, что ваша RDMS, вероятно, сделает запрос недействительным как кандидата для сохранения в своем кэше запросов, если он у него есть.
Как насчет
WHERE
DATEDIFF(d, Received, GETDATE()) = 0
Это не лучший способ. См. Сообщение Марка Грейвелла, чтобы узнать лучше.
Обычно я бы использовал решение, предложенное Томалаком, но если вы действительно отчаянно нуждаетесь в производительности, лучшим вариантом может быть добавление дополнительного индексированного поля ReceivedDataPartOnly, которое будет хранить данные без временной части, а затем использовать запрос
declare @today as datetime
set @today = datediff(d, 0, getdate())
select
count(job) as jobs
from
dbo.job
where
received_DatePartOnly = @today
Решение, предложенное Томалаком, далеко не лучшее.
Если вам нужна производительность, вам нужно, чтобы индекс непосредственный попадал в индекс без использования ЦП и т. д. На строку; поэтому я бы вычислил диапазон первый, а затем использовал бы простой запрос WHERE. Я не знаю, какой db вы используете, но в SQL Server работает следующее:
// ... where @When is the date-and-time we have (perhaps from GETDATE())
DECLARE @DayStart datetime, @DayEnd datetime
SET @DayStart = CAST(FLOOR(CAST(@When as float)) as datetime) -- get day only
SET @DayEnd = DATEADD(d, 1, @DayStart)
SELECT COUNT(Job) AS Jobs
FROM dbo.Job
WHERE (Received >= @DayStart AND Received < @DayEnd)
Я не совсем понимаю, что вы подразумеваете под "прямым" попаданием в индекс? если вы просто говорите о том, что есть вычисление на «другой» стороне оператора предиката, вместо того, чтобы предварительно вычислить его перед выполнением индекса, то либо A) вычисленное значение основано на каком-либо другом столбце в таблице и не то же самое для сгенерированной строки, поэтому она должна быть в sql, или B) если это одно и то же значение для каждой сгенерированной строки, обработчик запросов все равно предварительно вычислит ее, поэтому она БУДЕТ вычисляться только один раз, нет независимо от того, сколько строк выдает запрос.
Сравните две даты после преобразования в тот же формат, как показано ниже.
where CONVERT(varchar, createddate, 1) = CONVERT(varchar, getdate(), 1);
Обратите внимание, что выбранный вами ответ НЕ является лучшим способом производительности. Ваш путь близок к правильному, вам просто нужно переключиться на> = и <, а не использовать BETWEEN (что похоже на <= во втором условии и неверно).