sql >> Database >  >> RDS >> Sqlserver

Hoe het tijdgedeelte van een datetime-waarde (SQL Server) te verwijderen?

SQL Server 2008 en hoger

In SQL Server 2008 en hoger is de snelste manier natuurlijk Convert(date, @date) . Dit kan worden teruggedraaid naar een datetime of datetime2 indien nodig.

Wat is echt het beste in SQL Server 2005 en ouder?

Ik heb inconsistente beweringen gezien over wat het snelst is om de tijd vanaf een datum in SQL Server af te kappen, en sommige mensen zeiden zelfs dat ze getest hebben, maar mijn ervaring is anders. Dus laten we wat strengere tests doen en iedereen het script geven, zodat als ik fouten maak, mensen me kunnen corrigeren.

Float-conversies zijn niet nauwkeurig

Ten eerste zou ik wegblijven van het converteren van datetime naar float , omdat het niet correct wordt geconverteerd. Je komt misschien weg met het nauwkeurig uitvoeren van het time-removal-ding, maar ik denk dat het een slecht idee is om het te gebruiken omdat het impliciet aan ontwikkelaars communiceert dat dit een veilige operatie is en het is niet . Kijk eens:

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

Dit is niet iets dat we mensen zouden moeten leren in onze code of in onze online voorbeelden.

Het is ook niet eens de snelste manier!

Bewijs – Prestatietests

Als je zelf wat tests wilt uitvoeren om te zien hoe de verschillende methoden het echt doen, dan heb je dit setup-script nodig om de tests verderop uit te voeren:

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

Houd er rekening mee dat hierdoor een tabel van 427,57 MB in uw database wordt gemaakt en het uitvoeren ervan ongeveer 15-30 minuten duurt. Als uw database klein is en is ingesteld op 10% groei, duurt het langer dan wanneer u eerst groot genoeg wordt.

Nu voor het eigenlijke prestatietestscript. Houd er rekening mee dat het doelbewust is om geen rijen terug te sturen naar de client, omdat dit waanzinnig duur is voor 26 miljoen rijen en de prestatieverschillen tussen de methoden zou verbergen.

Prestatieresultaten

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;

Enkele rambling-analyse

Enkele opmerkingen hierover. Allereerst, als u alleen een GROUP BY of een vergelijking uitvoert, hoeft u niet terug te converteren naar datetime . U kunt dus wat CPU besparen door dat te vermijden, tenzij u de uiteindelijke waarde nodig heeft voor weergavedoeleinden. U kunt zelfs GROUPEREN OP de niet-geconverteerde waarde en de conversie alleen in de SELECT-clausule plaatsen:

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

Zie ook hoe de numerieke conversies slechts iets meer tijd kosten om terug te converteren naar datetime , maar de varchar conversie bijna verdubbeld? Dit onthult het deel van de CPU dat is gewijd aan datumberekening in de query's. Er zijn delen van het CPU-gebruik waarbij geen datumberekening is betrokken, en dit lijkt iets in de buurt van 19875 ms te zijn in de bovenstaande zoekopdrachten. Dan kost de conversie een extra bedrag, dus als er twee conversies zijn, wordt dat bedrag ongeveer twee keer opgebruikt.

Nader onderzoek laat zien dat in vergelijking met Convert(, 112) , de Convert(, 101) query heeft wat extra CPU-kosten (omdat het een langere varchar gebruikt) ?), omdat de tweede conversie terug naar date kost niet zoveel als de eerste conversie naar varchar , maar met Convert(, 112) het ligt dichter bij dezelfde 20000 ms CPU-basiskosten.

Hier zijn de berekeningen van de CPU-tijd die ik heb gebruikt voor de bovenstaande analyse:

     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
  • ronde is de CPU-tijd voor een retourtje terug naar datetime .

  • enkel is CPU-tijd voor een enkele conversie naar het alternatieve gegevenstype (degene die als neveneffect heeft dat het tijdgedeelte wordt verwijderd).

  • basis is de berekening van het aftrekken van single het verschil tussen de twee aanroepingen:single - (round - single) . Het is een margecijfer dat de conversie van en naar dat gegevenstype en datetime . aanneemt is in beide richtingen ongeveer hetzelfde. Het lijkt erop dat deze aanname niet perfect is, maar in de buurt komt, omdat de waarden allemaal dicht bij 20000 ms liggen, met slechts één uitzondering.

Nog een interessant ding is dat de basiskosten bijna gelijk zijn aan de enkele Convert(date) methode (die bijna 0 kosten moet zijn, omdat de server intern het gehele daggedeelte kan extraheren uit de eerste vier bytes van de datetime gegevenstype).

Conclusie

Dus hoe het eruit ziet is dat de eenrichtings varchar conversiemethode duurt ongeveer 1,8 μs en de single-directionele DateDiff methode duurt ongeveer 0,18 μs. Ik baseer dit op de meest conservatieve "basis CPU"-tijd in mijn testen van 18458 ms totaal voor 25.920.000 rijen, dus 23218 ms / 25920000 =0,18 μs. De schijnbare 10x verbetering lijkt veel, maar het is eerlijk gezegd vrij klein totdat je te maken hebt met honderdduizenden rijen (617k rijen =1 seconde besparing).

Zelfs gezien deze kleine absolute verbetering, is naar mijn mening de DateAdd methode wint omdat het de beste combinatie is van prestatie en duidelijkheid. Het antwoord dat een "magisch getal" van 0.50000004 . vereist gaat op een dag iemand bijten (vijf nullen of zes???), en het is moeilijker te begrijpen.

Aanvullende opmerkingen

Als ik wat tijd heb ga ik 0.50000004 veranderen naar '12:00:00.003' en kijk hoe het gaat. Het wordt geconverteerd naar dezelfde datetime waarde en ik vind het veel gemakkelijker te onthouden.

Voor degenen die geïnteresseerd zijn, de bovenstaande tests zijn uitgevoerd op een server waar @@Version het volgende retourneert:

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 op Windows NT 5.2 (Build 3790:Service Pack 2)



  1. NEXT_DAY() Functie in Oracle

  2. Verbind ODBC-toepassingen op Windows met QuickBooks Online

  3. Hoe de waitevent Geschiedenis van de Oracle-sessie te vinden

  4. Gegevens groeperen met de functies OVER en PARTITION BY