Alla befintliga (fungerande) svar har ett av två problem:
- De kommer att ignorera index i kolumnen som söks igenom
- Kommer (potentiellt) att välja data som inte är avsedd, vilket i tysthet korrumperar dina resultat.
1. Ignorerade index:
För det mesta, när en kolumn som söks igenom har en funktion anropad (inklusive implicit, som för CAST
), måste optimeraren ignorera index i kolumnen och söka igenom varje post. Här är ett snabbt exempel:
Vi har att göra med tidsstämplar, och de flesta RDBMS:er tenderar att lagra denna information som ett ökande värde av något slag, vanligtvis en long
eller BIGINTEGER
antal milli-/nanosekunder. Den aktuella tiden ser alltså ut/lagras så här:
1402401635000000 -- 2014-06-10 12:00:35.000000 GMT
Du ser inte värdet 'År' ('2014'
). ) där inne, gör du? Det finns faktiskt en hel del komplicerad matematik att översätta fram och tillbaka. Så om du anropar någon av funktionerna för extraktion/datumdel i den sökta kolumnen måste servern utföra all den matematiken bara för att ta reda på om du kan inkludera den i resultaten. På små tabeller är detta inte ett problem, men när andelen rader som valts minskar blir detta ett större och större avlopp. I det här fallet gör du det en andra gång för att fråga om MONTH
... ja, ni förstår bilden.
2. Oavsiktlig data:
Beroende på den specifika versionen av SQL Server och kolumndatatyper, med BETWEEN
(eller liknande inklusive övre gränsintervall:<=
) kan resultera i att fel data väljs. I grund och botten kan det sluta med att du inkluderar data från midnatt "nästa" dag, eller exkluderar någon del av den "aktuella" dagens register.
Vad du bör gör:
Så vi behöver ett sätt som är säkert för vår data och kommer att använda index (om det är genomförbart). Det korrekta sättet är då av formen:
WHERE date_created >= @startOfPreviousMonth AND date_created < @startOfCurrentMonth
Med tanke på att det bara är en månad, @startOfPreviousMonth
kan enkelt ersättas med/härledas av:
DATEADD(month, -1, @startOCurrentfMonth)
Om du behöver härleda start-of-current-month i servern kan du göra det via följande:
DATEADD(month, DATEDIFF(month, 0, CURRENT_TIMESTAMP), 0)
Ett snabbt ord av förklaring här. Den initiala DATEDIFF(...)
kommer att få skillnaden mellan början av den nuvarande eran (0001-01-01
- AD, CE, vad som helst), returnerar i huvudsak ett stort heltal. Detta är antalet månader till början av aktuellt månad. Vi lägger sedan till detta nummer till början av eran, som är i början av den givna månaden.
Så ditt fullständiga skript kan/bör se ut som följande:
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
Alla datumoperationer utförs alltså bara en gång, på ett värde; Optimizern är gratis att använda index, och inga felaktiga data kommer att inkluderas.