Усі існуючі (робочі) відповіді мають одну з двох проблем:
- Вони будуть ігнорувати індекси у стовпці, який шукають
- Воля (потенційно) відбирає дані, які не призначені, мовчки псуючи ваші результати.
1. Ігноровані індекси:
Здебільшого, коли в стовпці, який шукається, є функція, що викликається (в тому числі неявно, наприклад, for CAST
), оптимізатор повинен ігнорувати індекси в стовпці та шукати кожен запис. Ось короткий приклад:
Ми маємо справу з мітками часу, і більшість СУБД, як правило, зберігають цю інформацію як зростаюче певне значення, як правило, long
або BIGINTEGER
кількість мілі- / наносекунд. Поточний час таким чином виглядає / зберігається таким чином:
1402401635000000
Ви там не бачите значення "Рік" ( '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)
AND date_created < @startOfCurrentMonth
Таким чином, всі операції з датою виконуються лише один раз, з одним значенням; оптимізатор може безкоштовно використовувати індекси, і жодні неправильні дані не включатимуться.