Alle bestaande (werkende) antwoorden hebben een van de volgende twee problemen:
- Ze negeren indices in de kolom waarin wordt gezocht
- Het zal (potentieel) gegevens selecteren die niet bedoeld zijn, waardoor uw resultaten in stilte worden beschadigd.
Voor het grootste deel, wanneer een kolom die wordt doorzocht een functie heeft die wordt aangeroepen (inclusief impliciet, zoals voor CAST
), moet de optimizer indexen op de kolom negeren en door elk record zoeken. Hier is een snel voorbeeld:
We hebben te maken met tijdstempels en de meeste RDBMS'en hebben de neiging om deze informatie op te slaan als een of andere toenemende waarde, meestal een long
of BIGINTEGER
telling van milli-/nanoseconden. De huidige tijd ziet/wordt dus als volgt opgeslagen:
1402401635000000 -- 2014-06-10 12:00:35.000000 GMT
U ziet de waarde 'Jaar' niet ('2014'
) daarbinnen, jij ook? In feite is er nogal wat ingewikkelde wiskunde om heen en weer te vertalen. Dus als u een van de extractie-/datumgedeeltefuncties in de gezochte kolom aanroept, moet de server al die wiskunde uitvoeren om erachter te komen of u het in de resultaten kunt opnemen. Op kleine tafels is dit geen probleem, maar naarmate het percentage geselecteerde rijen afneemt, wordt dit een steeds grotere afvoer. In dit geval doe je het een tweede keer door te vragen naar MONTH
... nou, je snapt het wel.
Afhankelijk van de specifieke versie van SQL Server en kolomgegevenstypes, met behulp van BETWEEN
(of vergelijkbare inclusief bovengrenzen:<=
) kan ertoe leiden dat de verkeerde gegevens worden geselecteerd. In wezen neemt u mogelijk gegevens op van middernacht van de "volgende" dag, of sluit u een deel van de records van de "huidige" dag uit.
Wat u moet doen:
We hebben dus een manier nodig die veilig is voor onze gegevens en die gebruikmaakt van indices (indien haalbaar). De juiste manier is dan van de vorm:
WHERE date_created >= @startOfPreviousMonth AND date_created < @startOfCurrentMonth
Aangezien er maar één maand is, @startOfPreviousMonth
kan gemakkelijk worden vervangen door/afgeleid door:
DATEADD(month, -1, @startOCurrentfMonth)
Als u het begin van de huidige maand in de server moet afleiden, kunt u dit als volgt doen:
DATEADD(month, DATEDIFF(month, 0, CURRENT_TIMESTAMP), 0)
Hier even een korte uitleg. De initiële DATEDIFF(...)
krijgt het verschil tussen het begin van het huidige tijdperk (0001-01-01
- AD, CE, wat dan ook), die in wezen een groot geheel getal retourneert. Dit is het aantal maanden tot het begin van de huidige maand. We voegen dit getal vervolgens toe aan het begin van het tijdperk, dat is aan het begin van de opgegeven maand.
Dus je volledige script zou er ongeveer als volgt uit kunnen/moeten zien:
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
Alle datumbewerkingen worden dus maar één keer uitgevoerd, op één waarde; de optimizer is vrij om indexen te gebruiken en er worden geen onjuiste gegevens opgenomen.