Всички съществуващи (работещи) отговори имат един от двата проблема:
- Те ще игнорират индекси на колоната, която се търси
- Той (потенциално) ще избере данни, които не са предназначени, като безшумно повреди резултатите ви.
1. Игнорирани индекси:
В по-голямата си част, когато колона, която се търси, има извикана функция (включително имплицитно, като за CAST
), оптимизаторът трябва да игнорира индекси в колоната и да търси във всеки запис. Ето един бърз пример:
Имаме работа с времеви отпечатъци и повечето RDBMS са склонни да съхраняват тази информация като нарастваща стойност от някакъв вид, обикновено long
или BIGINTEGER
брой мили-/наносекунди. По този начин текущото време изглежда/се съхранява така:
1402401635000000 -- 2014-06-10 12:00:35.000000 GMT
Не виждате стойността „Година“ ('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
Всички операции с дата се изпълняват само веднъж, върху една стойност; оптимизаторът е свободен да използва индекси и няма да бъдат включени неправилни данни.