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

SQL-Server ZWISCHEN

Eine Möglichkeit zu finden, BETWEEN mit der Tabelle so zu verwenden, wie sie ist, wird funktionieren, aber in jedem Fall eine schlechtere Leistung bringen:

  • Es wird bestenfalls mehr CPU verbrauchen, um irgendeine Art von Berechnung an den Zeilen durchzuführen, anstatt mit ihnen als Datumsangaben zu arbeiten.
  • Im schlimmsten Fall erzwingt es einen Tabellenscan für jede Zeile in der Tabelle, aber wenn Ihre Spalten Indizes haben, dann ist mit der richtigen Abfrage eine Suche möglich. Dies könnte ein RIESIGER Leistungsunterschied sein, da das Erzwingen der Einschränkungen in einer BETWEEN-Klausel die Verwendung des Index deaktiviert.

Ich schlage stattdessen Folgendes vor, wenn Sie einen Index für Ihre Datumsspalten haben und sich überhaupt um die Leistung kümmern:

DECLARE
   @FromDate date = '20111101',
   @ToDate date = '20120201';

SELECT *
FROM dbo.YourTable T
WHERE
   (
      T.[Year] > Year(@FromDate)
      OR (    
         T.[Year] = Year(@FromDate)
         AND T.[Month] >= Month(@FromDate)
      )
   ) AND (
      T.[Year] < Year(@ToDate)
      OR (
         T.[Year] = Year(@ToDate)
         AND T.[Month] <= Month(@ToDate)
      )
   );

Es ist jedoch verständlich, dass Sie eine solche Konstruktion nicht verwenden möchten, da sie sehr umständlich ist. Hier ist also eine Kompromissabfrage, die zumindest numerische Berechnungen verwendet und weniger CPU verbraucht als die Berechnung der Datum-zu-String-Konvertierung (obwohl nicht genug weniger, um den erzwungenen Scan auszugleichen, der das eigentliche Leistungsproblem darstellt).

SELECT *
FROM dbo.YourTable T
WHERE
   T.[Year] * 100 + T.[Month] BETWEEN 201111 AND 201202;

Wenn Sie einen Index für Year haben , können Sie einen großen Schub erhalten, indem Sie die Abfrage wie folgt senden, die die Möglichkeit hat, zu suchen:

SELECT *
FROM dbo.YourTable T
WHERE
   T.[Year] * 100 + T.[Month] BETWEEN 201111 AND 201202
   AND T.[Year] BETWEEN 2011 AND 2012; -- allows use of an index on [Year]

Dies unterbricht jedoch Ihre Anforderung, ein einzelnes BETWEEN zu verwenden Ausdruck, es ist nicht allzu viel schmerzhafter und wird mit dem Year sehr gut funktionieren index.

Sie können auch Ihren Tisch ändern. Ehrlich gesagt ist es nicht gut, separate Zahlen für Ihre Datumsteile anstelle einer einzelnen Spalte mit einem Datumsdatentyp zu verwenden. Der Grund, warum es nicht gut ist, liegt in dem genauen Problem, mit dem Sie gerade konfrontiert sind – es ist sehr schwer abzufragen.

In einigen Data-Warehousing-Szenarien, in denen das Einsparen von Bytes sehr wichtig ist, könnte ich mir Situationen vorstellen, in denen Sie das Datum als Zahl speichern könnten (z. B. 201111 ) aber das ist nicht zu empfehlen. Das Beste Die Lösung besteht darin, Ihre Tabelle so zu ändern, dass sie Datumsangaben verwendet, anstatt den numerischen Wert des Monats und des Jahres aufzuteilen. Speichern Sie einfach den ersten Tag des Monats und erkennen Sie, dass er für den gesamten Monat steht.

Wenn eine Änderung der Art und Weise, wie Sie diese Spalten verwenden, keine Option ist, Sie Ihre Tabelle aber dennoch ändern können, können Sie eine dauerhaft berechnete Spalte hinzufügen:

ALTER Table dbo.YourTable
   ADD ActualDate AS (DateAdd(year, [Year] - 1900, DateAdd(month, [Month], '18991201')))
   PERSISTED;

Damit können Sie einfach Folgendes tun:

SELECT *
FROM dbo.YourTable
WHERE
   ActualDate BETWEEN '20111101' AND '20120201';

Die PERSISTED Das Schlüsselwort bedeutet, dass Sie zwar immer noch einen Scan erhalten, aber keine Berechnung für jede Zeile durchführen müssen, da der Ausdruck bei jedem INSERT oder UPDATE berechnet und in der Zeile gespeichert wird. Aber Sie können Holen Sie sich eine Suche, wenn Sie dieser Spalte einen Index hinzufügen, wodurch sie sehr gut funktioniert (obwohl dies alles in allem immer noch nicht so ideal ist, wie die Verwendung einer tatsächlichen Datumsspalte zu ändern, da dies mehr Platz beansprucht und sich auf INSERTs auswirkt und UPDATE):

CREATE NONCLUSTERED INDEX IX_YourTable_ActualDate ON dbo.YourTable (ActualDate);

Fazit:Wenn Sie wirklich nichts an der Tabelle ändern können, dann müssen Sie irgendwie einen Kompromiss eingehen. Es wird nicht möglich sein, die gewünschte einfache Syntax zu erhalten, die auch gut funktioniert, wenn Ihre Daten in separaten Spalten aufgeteilt gespeichert werden.