Все существующие (рабочие) ответы имеют одну из двух проблем:
- Они будут игнорировать индексы для столбца, в котором выполняется поиск
- Завещание (потенциально) выберет данные, которые не предназначены, молча повреждая ваши результаты.
1. Игнорируемые индексы:
В большинстве случаев, когда для искомого столбца вызывается функция (в том числе неявно, как для CAST
), оптимизатор должен игнорировать индексы столбца и выполнять поиск по каждой записи. Вот быстрый пример:
Мы имеем дело с метками времени, и большинство РСУБД, как правило, хранят эту информацию в виде некоторого возрастающего значения, обычно счетчика long
или BIGINTEGER
в милли / наносекундах. Текущее время, таким образом, выглядит / сохраняется так:
1402401635000000 -- 2014-06-10 12:00:35.000000 GMT
Вы не видите значения 'Year' ('2014'
) там, не так ли? На самом деле, есть довольно сложная математика для перевода туда-сюда. Таким образом, если вы вызываете какую-либо функцию извлечения / даты в столбце поиска, сервер должен выполнить всю эту математику, чтобы выяснить, можете ли вы включить ее в результаты. Для небольших таблиц это не проблема, но с уменьшением процента выбранных строк это становится все больше и больше. Затем в этом случае вы делаете это во второй раз, спрашивая о MONTH
... ну, вы поняли.
2. Непреднамеренные данные:
В зависимости от конкретной версии SQL Server и типов данных столбцов, использование BETWEEN
(или аналогичных включающих верхних диапазонов: <=
) может привести к неправильному выбору данных . По сути, вы можете в конечном итоге включить данные с полуночи «следующего» дня или исключить некоторую часть записей «текущего» дня.
Что вы должны делать:
Итак, нам нужен способ, который был бы безопасен для наших данных, и мы будем использовать индексы (если это возможно). Правильный путь имеет вид:
WHERE date_created >= @startOfPreviousMonth AND date_created < @startOfCurrentMonth
Учитывая, что есть только один месяц, @startOfPreviousMonth
может быть легко заменен / получен с помощью:
DATEADD(month, -1, @startOCurrentfMonth)
Если вам нужно определить начало текущего месяца на сервере, вы можете сделать это следующим образом:
DATEADD(month, DATEDIFF(month, 0, CURRENT_TIMESTAMP), 0)
Быстрое объяснение здесь. Начальный DATEDIFF(...)
получит разницу между началом текущей эры (0001-01-01
- AD, CE, что угодно), по сути, возвращая большое целое число. Это количество месяцев до начала текущего месяца. Затем мы добавляем это число в начало эры, которая находится в начале данного месяца.
Таким образом, ваш полный сценарий может / должен выглядеть примерно так:
DECLARE @startOfCurrentMonth DATETIME
SET @startOfCurrentMonth = DATEADD(month, DATEDIFF(month, 0, CURRENT_TIMESTAMP), 0)
SELECT *
FROM Member
WHERE date_created >= DATEADD(month, -1, @startOfCurrentMonth) -- this was originally misspelled
AND date_created < @startOfCurrentMonth
Таким образом, все операции с датами выполняются только один раз, с одним значением; оптимизатор может свободно использовать индексы, и никакие неверные данные не будут включены.