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

Wie entferne ich den Zeitteil eines datetime-Werts (SQL Server)?

SQL Server 2008 und höher

In SQL Server 2008 und höher ist der schnellste Weg natürlich Convert(date, @date) . Dies kann auf ein datetime zurückgesetzt werden oder datetime2 falls nötig.

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

Ich habe inkonsistente Behauptungen darüber gesehen, was am schnellsten ist, um die Zeit von einem Datum in SQL Server abzuschneiden, und einige Leute sagten sogar, sie hätten Tests durchgeführt, aber meine Erfahrung war anders. Also lasst uns ein paar strengere Tests durchführen und jedem das Skript geben, damit die Leute mich korrigieren können, wenn ich Fehler mache.

Float-Conversions sind nicht genau

Erstens würde ich davon Abstand nehmen, datetime umzuwandeln zu float , weil es nicht richtig konvertiert wird. Sie kommen vielleicht damit durch, das Zeitentfernungs-Ding genau zu machen, aber ich denke, es ist eine schlechte Idee, es zu verwenden, weil es den Entwicklern implizit mitteilt, dass dies eine sichere Operation ist und das ist es 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!

Nachweis – Leistungstest

Wenn Sie selbst einige Tests durchführen möchten, um zu sehen, wie sich die verschiedenen Methoden wirklich verhalten, 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 Tabelle mit 427,57 MB in Ihrer Datenbank erstellt wird und die Ausführung etwa 15–30 Minuten dauert. Wenn Ihre Datenbank klein und auf 10 % Wachstum eingestellt ist, dauert es länger, als wenn Sie zuerst groß genug dimensionieren.

Nun zum eigentlichen Leistungstestskript. Bitte beachten Sie, dass es sinnvoll ist, Zeilen nicht 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 Analyse

Einige Anmerkungen dazu. Zunächst einmal, wenn Sie nur ein GROUP BY oder einen Vergleich durchführen, müssen Sie nicht zurück in datetime konvertieren . Sie können also etwas CPU sparen, indem Sie dies vermeiden, es sei denn, Sie benötigen den endgültigen Wert für Anzeigezwecke. Sie können sogar den nicht konvertierten Wert GROUP BY verwenden 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)

Sehen Sie auch, wie die numerischen Konvertierungen nur geringfügig länger dauern, um sie wieder in datetime zu konvertieren , aber die varchar Konversion fast verdoppelt? Dies zeigt den Anteil der CPU, der für die Datumsberechnung in den Abfragen verwendet wird. Es gibt Teile der CPU-Auslastung, die keine Datumsberechnung beinhalten, und dies scheint in den obigen Abfragen ungefähr 19875 ms zu sein. Dann erfordert die Konvertierung einen zusätzlichen Betrag. Wenn es also zwei Konvertierungen gibt, wird dieser Betrag ungefähr zweimal verbraucht.

Weitere Untersuchungen zeigen dies im Vergleich zu Convert(, 112) , die Convert(, 101) Abfrage hat einige zusätzliche CPU-Kosten (da sie eine längere varchar verwendet ?), weil die zweite Konvertierung zurück zu date kostet nicht so viel wie die anfängliche Umstellung auf varchar , aber mit Convert(, 112) es liegt näher an denselben CPU-Basiskosten 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
  • rund ist die CPU-Zeit für einen Roundtrip zurück zu datetime .

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

  • Basis ist die Berechnung der Subtraktion von single der Unterschied zwischen den beiden Aufrufen:single - (round - single) . Es ist eine ungefähre Zahl, die die Konvertierung zu und von diesem Datentyp und datetime annimmt ist in beiden Richtungen ungefähr gleich. Es scheint, dass diese Annahme nicht perfekt ist, aber nah dran ist, da die Werte mit nur einer Ausnahme alle nahe bei 20.000 ms liegen.

Eine weitere interessante Sache ist, dass die Grundkosten fast gleich dem einzelnen Convert(date) sind Methode (die fast 0 kosten muss, da der Server intern den ganzzahligen Tagesteil direkt aus den ersten vier Bytes von datetime extrahieren kann Datentyp).

Fazit

Es sieht also so aus, dass die in eine Richtung verlaufende varchar Die Konvertierungsmethode dauert etwa 1,8 μs und der unidirektionale DateDiff Methode dauert etwa 0,18 μs. Ich basiere dies auf der konservativsten „Basis-CPU“-Zeit in meinen Tests von insgesamt 18.458 ms für 25.920.000 Zeilen, also 23.218 ms/25920000 =0,18 μs. Die scheinbare 10-fache Verbesserung scheint viel zu sein, ist aber ehrlich gesagt ziemlich gering, bis Sie es mit Hunderttausenden von Zeilen zu tun haben (617.000 Zeilen =1 Sekunde Einsparung).

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

Zusätzliche Hinweise

Wenn ich etwas Zeit habe, werde ich 0.50000004 ändern zu '12:00:00.003' und sehen, wie es tut. Es wird in dasselbe datetime konvertiert Wert und ich finde es viel leichter zu merken.

Für diejenigen, die daran interessiert sind, wurden die obigen Tests auf einem Server ausgeführt, auf dem @@Version Folgendes zurückgibt:

Microsoft SQL Server 2008 (RTM) – 10.0.1600.22 (Intel X86) 9. Juli 2008 14:43:34 Copyright (c) 1988-2008 Microsoft Corporation Standard Edition auf Windows NT 5.2 (Build 3790:Service Pack 2)