Alle vorhandenen (Arbeits-) Antworten haben eines von zwei Problemen:
- Sie ignorieren Indizes in der gesuchten Spalte
- Das wird (möglicherweise) Daten auswählen, die nicht beabsichtigt sind, und Ihre Ergebnisse stillschweigend verfälschen.
1. Ignorierte Indizes:
Wenn für eine durchsuchte Spalte eine Funktion aufgerufen wird (einschließlich implizit wie für CAST
), muss der Optimierer zum größten Teil die Indizes in der Spalte ignorieren und jeden Datensatz durchsuchen. Hier ist ein kurzes Beispiel:
Wir haben es mit Zeitstempeln zu tun, und die meisten RDBMS speichern diese Informationen in der Regel als zunehmenden Wert, normalerweise als long
oder BIGINTEGER
in Milli- / Nanosekunden. Die aktuelle Zeit sieht also so aus / wird wie folgt gespeichert:
1402401635000000
Sie sehen dort nicht den 'Year'-Wert ( '2014'
), oder? Tatsächlich gibt es einiges an komplizierter Mathematik, die man hin und her übersetzen kann. Wenn Sie also eine der Extraktions- / Datumsteilfunktionen in der gesuchten Spalte aufrufen, muss der Server all diese Berechnungen durchführen, um herauszufinden, ob Sie sie in die Ergebnisse aufnehmen können. Bei kleinen Tabellen ist dies kein Problem, aber wenn der Prozentsatz der ausgewählten Zeilen abnimmt, wird dies zu einem immer größeren Abfluss. Dann machen Sie es in diesem Fall ein zweites Mal, um nach MONTH
... zu fragen. Nun , Sie bekommen das Bild.
2. Unbeabsichtigte Daten:
Abhängig von der jeweiligen Version von SQL Server und Datentypen von Spalten, mit BETWEEN
(oder ähnlich inklusive oberen gebundenen Bereichen: <=
) kann in den falschen Daten führen ausgewählt werden . Im Wesentlichen können Sie Daten ab Mitternacht des "nächsten" Tages einschließen oder einen Teil der Aufzeichnungen des "aktuellen" Tages ausschließen.
Was Sie sollten dabei sein:
Wir brauchen also einen Weg, der für unsere Daten sicher ist und Indizes verwendet (falls möglich). Der richtige Weg ist dann von der Form:
WHERE date_created >= @startOfPreviousMonth AND date_created < @startOfCurrentMonth
Da es nur einen Monat gibt, @startOfPreviousMonth
kann es leicht ersetzt werden durch:
DATEADD(month, -1, @startOCurrentfMonth)
Wenn Sie den Beginn des aktuellen Monats auf dem Server ableiten müssen, können Sie dies folgendermaßen tun:
DATEADD(month, DATEDIFF(month, 0, CURRENT_TIMESTAMP), 0)
Ein kurzes Wort zur Erklärung hier. Die Initiale DATEDIFF(...)
wird den Unterschied zwischen dem Beginn der aktuellen Ära ( 0001-01-01
- AD, CE, was auch immer) erkennen und im Wesentlichen eine große ganze Zahl zurückgeben. Dies ist die Anzahl der Monate bis zum Beginn des aktuellen Monats. Wir addieren diese Zahl dann zum Beginn der Ära, die zu Beginn des jeweiligen Monats liegt.
Ihr vollständiges Skript könnte / sollte also wie folgt aussehen:
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
Alle Datumsoperationen werden daher nur einmal mit einem Wert ausgeführt. Dem Optimierer steht es frei, Indizes zu verwenden, und es werden keine falschen Daten aufgenommen.