Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Holen Sie sich die Aufzeichnungen des letzten Monats in SQL Server

Alle vorhandenen (funktionierenden) Antworten haben eines von zwei Problemen:

  1. Sie ignorieren Indizes in der durchsuchten Spalte
  2. Der wird (möglicherweise) Daten auswählen, die nicht beabsichtigt sind, und Ihre Ergebnisse stillschweigend verfälschen.

1. Ignorierte Indizes:

Wenn für eine zu durchsuchende Spalte eine Funktion aufgerufen wird (einschließlich implizit, wie für CAST ), muss der Optimierer 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 neigen dazu, diese Informationen als ansteigenden Wert zu speichern, normalerweise als long oder BIGINTEGER Anzahl Milli-/Nanosekunden. Die aktuelle Uhrzeit sieht also so aus/wird gespeichert:

1402401635000000  -- 2014-06-10 12:00:35.000000 GMT

Der Wert für das Jahr wird nicht angezeigt ('2014'). ) da drin, oder? Tatsächlich gibt es einiges an komplizierter Mathematik, die hin und her übersetzt werden muss. Wenn Sie also eine der Extraktions-/Datumsteilfunktionen für die gesuchte Spalte aufrufen, muss der Server all diese Berechnungen durchführen, nur 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 einer immer größeren Belastung. Dann tun Sie es in diesem Fall ein zweites Mal, um nach MONTH zu fragen ... gut, Sie bekommen das Bild.

2. Unbeabsichtigte Daten:

Abhängig von der jeweiligen Version von SQL Server und den Spaltendatentypen mit BETWEEN (oder ähnliche einschließliche Obergrenzenbereiche:<= ) kann dazu führen, dass die falschen Daten ausgewählt werden. Im Wesentlichen schließen Sie möglicherweise Daten ab Mitternacht des „nächsten“ Tages ein oder schließen einen Teil der Datensätze des „aktuellen“ Tages aus.

Was Sie sollten tun:

Wir brauchen also einen Weg, der für unsere Daten sicher ist, und verwenden Indizes (falls möglich). Der korrekte Weg hat dann die Form:

WHERE date_created >= @startOfPreviousMonth AND date_created < @startOfCurrentMonth

Da es nur einen Monat gibt, @startOfPreviousMonth kann einfach ersetzt/abgeleitet werden von:

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)

Hier ein kurzes Wort zur Erklärung. Das anfängliche DATEDIFF(...) erhält die Differenz zwischen dem Beginn der aktuellen Ära (0001-01-01 - AD, CE, was auch immer), im Wesentlichen eine große Ganzzahl zurückgeben. Dies ist die Anzahl der Monate bis zum Beginn des aktuellen Monat. Wir addieren diese Zahl dann zum Beginn des Zeitalters, das am Beginn des angegebenen Monats liegt.

Ihr vollständiges Skript könnte/sollte also etwa so aussehen:

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 Datumsoperationen werden somit nur einmal auf einem Wert durchgeführt; Der Optimierer kann Indizes frei verwenden, und es werden keine falschen Daten aufgenommen.