Frage

Folgendes verwende ich:

SELECT CAST(FLOOR(CAST(getdate() as FLOAT)) as DATETIME)

Ich denke, es gibt möglicherweise einen besseren und eleganteren Weg.

Anforderungen:

  • Es muss so schnell wie möglich sein (je weniger Guss, desto besser).
  • Das Endergebnis muss a sein datetime Typ, keine Zeichenfolge.
War es hilfreich?

Lösung

SQL Server 2008 und höher

In SQL Server 2008 und höher ist dies natürlich der schnellste Weg Convert(date, @date).Dies kann auf a zurückgeführt werden datetime oder datetime2 Falls benötigt.

Was ist wirklich das Beste an SQL Server 2005 und älter?

Ich habe inkonsistente Behauptungen darüber gesehen, wie sich die Zeit ab einem Datum in SQL Server am schnellsten kürzen lässt, und einige Leute sagten sogar, sie hätten Tests durchgeführt, aber meine Erfahrung war anders.Lassen Sie uns also ein paar strengere Tests durchführen und jedem das Skript geben, damit mich die Leute korrigieren können, wenn ich Fehler mache.

Float-Konvertierungen sind nicht genau

Erstens würde ich von einer Konvertierung Abstand nehmen datetime Zu float, weil es nicht richtig konvertiert wird.Sie kommen vielleicht damit durch, die Sache mit der Zeitentfernung genau zu machen, aber ich halte es für eine schlechte Idee, sie zu verwenden, weil sie den Entwicklern implizit mitteilt, dass es sich um einen sicheren Vorgang handelt es ist nicht.Schau mal:

declare @d datetime;
set @d = '2010-09-12 00:00:00.003';
select Convert(datetime, Convert(float, @d));
-- result: 2010-09-12 00:00:00.000 -- oops

Das sollten wir den Leuten nicht in unserem Code oder in unseren Online-Beispielen beibringen.

Außerdem ist es nicht einmal der schnellste Weg!

Beweis – Leistungstests

Wenn Sie selbst einige Tests durchführen möchten, um zu sehen, wie sich die verschiedenen Methoden tatsächlich schlagen, benötigen Sie dieses Setup-Skript, um die Tests weiter unten auszuführen:

create table AllDay (Tm datetime NOT NULL CONSTRAINT PK_AllDay PRIMARY KEY CLUSTERED);
declare @d datetime;
set @d = DateDiff(Day, 0, GetDate());
insert AllDay select @d;
while @@ROWCOUNT != 0
   insert AllDay
   select * from (
      select Tm =
         DateAdd(ms, (select Max(DateDiff(ms, @d, Tm)) from AllDay) + 3, Tm)
      from AllDay
   ) X
   where Tm < DateAdd(Day, 1, @d);
exec sp_spaceused AllDay;  -- 25,920,000 rows

Bitte beachten Sie, dass dadurch eine 427,57 MB große Tabelle in Ihrer Datenbank erstellt wird und die Ausführung etwa 15 bis 30 Minuten dauert.Wenn Ihre Datenbank klein ist und auf 10 % Wachstum eingestellt ist, dauert es länger, als wenn Sie sie zuerst groß genug dimensionieren.

Nun zum eigentlichen Leistungstestskript.Bitte beachten Sie, dass es sinnvoll ist, keine Zeilen an den Client zurückzugeben, da dies bei 26 Millionen Zeilen wahnsinnig teuer ist und die Leistungsunterschiede zwischen den Methoden verbergen würde.

Leistungsergebnisse

set statistics time on;
-- (All queries are the same on io: logical reads 54712)
GO
declare
    @dd date,
    @d datetime,
    @di int,
    @df float,
    @dv varchar(10);

-- Round trip back to datetime
select @d = CONVERT(date, Tm) from AllDay; -- CPU time = 21234 ms,  elapsed time = 22301 ms.
select @d = CAST(Tm - 0.50000004 AS int) from AllDay; -- CPU = 23031 ms, elapsed = 24091 ms.
select @d = DATEDIFF(DAY, 0, Tm) from AllDay; -- CPU = 23782 ms, elapsed = 24818 ms.
select @d = FLOOR(CAST(Tm as float)) from AllDay; -- CPU = 36891 ms, elapsed = 38414 ms.
select @d = CONVERT(VARCHAR(8), Tm, 112) from AllDay; -- CPU = 102984 ms, elapsed = 109897 ms.
select @d = CONVERT(CHAR(8), Tm, 112) from AllDay; -- CPU = 103390 ms,  elapsed = 108236 ms.
select @d = CONVERT(VARCHAR(10), Tm, 101) from AllDay; -- CPU = 123375 ms, elapsed = 135179 ms.

-- Only to another type but not back
select @dd = Tm from AllDay; -- CPU time = 19891 ms,  elapsed time = 20937 ms.
select @di = CAST(Tm - 0.50000004 AS int) from AllDay; -- CPU = 21453 ms, elapsed = 23079 ms.
select @di = DATEDIFF(DAY, 0, Tm) from AllDay; -- CPU = 23218 ms, elapsed = 24700 ms
select @df = FLOOR(CAST(Tm as float)) from AllDay; -- CPU = 29312 ms, elapsed = 31101 ms.
select @dv = CONVERT(VARCHAR(8), Tm, 112) from AllDay; -- CPU = 64016 ms, elapsed = 67815 ms.
select @dv = CONVERT(CHAR(8), Tm, 112) from AllDay; -- CPU = 64297 ms,  elapsed = 67987 ms.
select @dv = CONVERT(VARCHAR(10), Tm, 101) from AllDay; -- CPU = 65609 ms, elapsed = 68173 ms.
GO
set statistics time off;

Einige weitschweifige Analysen

Einige Anmerkungen dazu.Wenn Sie nur ein GROUP BY oder einen Vergleich durchführen, ist zunächst keine Rückkonvertierung erforderlich datetime.Sie können also etwas CPU einsparen, indem Sie dies vermeiden, es sei denn, Sie benötigen den Endwert zu Anzeigezwecken.Sie können sogar den nicht konvertierten Wert mit GROUP BY versehen und die Konvertierung nur in die SELECT-Klausel einfügen:

select Convert(datetime, DateDiff(dd, 0, Tm))
from (select '2010-09-12 00:00:00.003') X (Tm)
group by DateDiff(dd, 0, Tm)

Beachten Sie auch, dass die numerische Konvertierung nur geringfügig länger dauert, um sie wieder zu konvertieren datetime, aber die varchar Conversion fast verdoppelt?Dies zeigt den Anteil der CPU, der für die Datumsberechnung in den Abfragen aufgewendet wird.Es gibt Teile der CPU-Auslastung, für die keine Datumsberechnung erforderlich ist. In den obigen Abfragen scheint dies etwa 19.875 ms zu sein.Dann erfordert die Konvertierung einen zusätzlichen Betrag. Wenn es also zwei Konvertierungen gibt, wird dieser Betrag ungefähr doppelt so stark verbraucht.

Weitere Untersuchungen zeigen, dass im Vergleich zu Convert(, 112), Die Convert(, 101) Die Abfrage hat einen gewissen zusätzlichen CPU-Aufwand (da sie eine längere Zeit in Anspruch nimmt). varchar?), weil die zweite Konvertierung zurück zu date kostet nicht so viel wie die Erstkonvertierung varchar, aber mit Convert(, 112) es liegt näher an den gleichen CPU-Grundkosten von 20.000 ms.

Hier sind die Berechnungen zur CPU-Zeit, die ich für die obige Analyse verwendet habe:

     method   round  single   base
-----------  ------  ------  -----
       date   21324   19891  18458
        int   23031   21453  19875
   datediff   23782   23218  22654
      float   36891   29312  21733
varchar-112  102984   64016  25048
varchar-101  123375   65609   7843
  • runden ist die CPU-Zeit für einen Roundtrip zurück datetime.

  • einzel ist die CPU-Zeit für eine einzelne Konvertierung in den alternativen Datentyp (derjenige, der den Nebeneffekt hat, dass der Zeitanteil entfernt wird).

  • Base ist die Berechnung des Subtrahierens von single Der Unterschied zwischen den beiden Aufrufen: single - (round - single).Es handelt sich um eine Richtzahl, die die Konvertierung in und aus diesem Datentyp voraussetzt datetime ist in beiden Richtungen ungefähr gleich.Es scheint, dass diese Annahme nicht perfekt ist, aber nahe kommt, da die Werte mit nur einer Ausnahme alle nahe bei 20.000 ms liegen.

Eine weitere interessante Sache ist, dass die Grundkosten fast den Einzelkosten entsprechen Convert(date) Methode (die fast 0 Kosten haben muss, da der Server intern den ganzzahligen Tagesanteil direkt aus den ersten vier Bytes extrahieren kann datetime Datentyp).

Abschluss

Es sieht also danach aus, dass es sich um eine einzige Richtung handelt varchar Die Konvertierungsmethode dauert etwa 1,8 μs und erfolgt in eine Richtung DateDiff Die Methode dauert etwa 0,18 μs.Ich stütze mich dabei auf die konservativste „Basis-CPU“-Zeit in meinen Tests von insgesamt 18.458 ms für 25.920.000 Zeilen, also 23.218 ms / 25920.000 = 0,18 μs.Die scheinbare 10-fache Verbesserung scheint viel zu sein, ist aber ehrlich gesagt ziemlich gering, bis man es mit Hunderttausenden von Zeilen zu tun hat (617.000 Zeilen = 1 Sekunde Ersparnis).

Selbst angesichts dieser kleinen absoluten Verbesserung ist meiner Meinung nach die DateAdd Methode gewinnt, weil sie die beste Kombination aus Leistung und Klarheit bietet.Die Antwort erfordert eine „magische Zahl“ von 0.50000004 wird eines Tages jemanden beißen (fünf Nullen oder sechs???), außerdem ist es schwerer zu verstehen.

Zusätzliche Bemerkungen

Wenn ich etwas Zeit habe, werde ich mich umziehen 0.50000004 Zu '12:00:00.003' und sehen Sie, wie es funktioniert.Es wird in dasselbe umgewandelt datetime Ich finde es viel einfacher, mich daran zu erinnern.

Für Interessierte: Die oben genannten Tests wurden auf einem Server ausgeführt, auf dem @@Version Folgendes zurückgibt:

Microsoft SQL Server 2008 (RTM) – 10.0.1600.22 (IntelServicepaket 2)

Andere Tipps

SQL Server 2008 hat eine neue Datum Datentyp und dies vereinfacht dieses Problem zu:

SELECT CAST(CAST(GETDATE() AS date) AS datetime)

Itzik Ben-Gan in DATETIME-Berechnungen, Teil 1 (SQL Server Magazine, Februar 2007) zeigt drei Methoden zur Durchführung einer solchen Konvertierung (vom langsamsten zum schnellsten;der Unterschied zwischen der zweiten und dritten Methode ist gering):

SELECT CAST(CONVERT(char(8), GETDATE(), 112) AS datetime)

SELECT DATEADD(day, DATEDIFF(day, 0, GETDATE()), 0)

SELECT CAST(CAST(GETDATE() - 0.50000004 AS int) AS datetime)

Ihre Technik (Werfen nach schweben) wird von einem Leser in der April-Ausgabe des Magazins vorgeschlagen.Seiner Meinung nach ist die Leistung mit der der oben vorgestellten zweiten Technik vergleichbar.

Dein CAST-FLOOR-CAST scheint bereits der optimale Weg zu sein, zumindest auf MS SQL Server 2005.

Einige andere Lösungen, die ich gesehen habe, verfügen über eine String-Konvertierung, z Select Convert(varchar(11), getdate(),101) in ihnen, was um den Faktor 10 langsamer ist.

Bitte versuche:

SELECT CONVERT(VARCHAR(10),[YOUR COLUMN NAME],105) [YOURTABLENAME]

SQL2005:Ich empfehle cast anstelle von dateadd.Zum Beispiel,

select cast(DATEDIFF(DAY, 0, datetimefield) as datetime)

durchschnittlich etwa 10 % Schneller auf meinem Datensatz, als

select DATEADD(DAY, DATEDIFF(DAY, 0, datetimefield), 0)

(und das Casting in smalldatetime war noch schneller)

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top