sql >> Databáze >  >> RDS >> Sqlserver

Jak odebrat časovou část hodnoty datetime (SQL Server)?

SQL Server 2008 a vyšší

V SQL Server 2008 a novějších je samozřejmě nejrychlejší způsob Convert(date, @date) . Toto lze přenést zpět na datetime nebo datetime2 v případě potřeby.

Co je skutečně nejlepší na SQL Server 2005 a starších?

Viděl jsem rozporuplná tvrzení o tom, co je nejrychlejší pro zkrácení času z data na serveru SQL Server, a někteří lidé dokonce řekli, že testovali, ale moje zkušenost byla jiná. Udělejme tedy přísnější testování a nechme scénář mít každý, takže pokud udělám nějaké chyby, lidé mě mohou opravit.

Plovoucí konverze nejsou přesné

Nejprve bych se držel dál od převodu datetime float , protože se nepřevádí správně. Možná vám projde, když uděláte věc s časovým odstraněním přesně, ale myslím si, že je špatný nápad to používat, protože to implicitně sděluje vývojářům, že se jedná o bezpečnou operaci a to není . Podívejte se:

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

To není něco, co bychom měli učit lidi v našem kódu nebo v našich příkladech online.

Navíc to není ani nejrychlejší způsob!

Důkaz – testování výkonu

Pokud chcete sami provést nějaké testy, abyste viděli, jak se různé metody skutečně skládají, pak budete potřebovat tento instalační skript, abyste testy spustili dále:

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

Upozorňujeme, že tím se ve vaší databázi vytvoří tabulka o velikosti 427,57 MB a její spuštění bude trvat přibližně 15–30 minut. Je-li vaše databáze malá a nastavená na 10% růst, bude to trvat déle, než kdybyste nejprve vytvořili dostatečně velkou velikost.

Nyní k samotnému skriptu testování výkonu. Vezměte prosím na vědomí, že je účelné nevracet řádky zpět klientovi, protože to je u 26 milionů řádků šíleně drahé a skrylo by to rozdíly ve výkonu mezi metodami.

Výsledky výkonu

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;

Některá zbytečná analýza

Pár poznámek k tomu. Za prvé, pokud pouze provádíte GROUP BY nebo porovnání, není třeba převádět zpět na datetime . Takže můžete ušetřit nějaké CPU tím, že se tomu vyhnete, pokud nepotřebujete konečnou hodnotu pro účely zobrazení. Můžete dokonce GROUP BY nepřevedenou hodnotu a převod umístit pouze do klauzule SELECT:

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

Podívejte se také, jak numerickým převodům trvá převod zpět na datetime o něco déle , ale varchar konverze téměř dvojnásobná? To odhaluje část CPU, která je věnována výpočtu data v dotazech. Existují části využití procesoru, které nezahrnují výpočet data, a zdá se, že ve výše uvedených dotazech je to něco blízkého 19875 ms. Pak konverze vyžaduje nějakou další částku, takže pokud dojde ke dvěma konverzím, tato částka se vyčerpá přibližně dvakrát.

Další zkoumání ukazuje, že ve srovnání s Convert(, 112) , Convert(, 101) dotaz má nějaké další náklady na CPU (protože používá delší varchar ?), protože druhý převod zpět na date nestojí tolik jako počáteční konverze na varchar , ale pomocí Convert(, 112) je blíže stejným základním nákladům na CPU 20 000 ms.

Zde jsou výpočty času CPU, které jsem použil pro výše uvedenou analýzu:

     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
  • kulaté je čas CPU pro zpáteční cestu zpět do datetime .

  • single je čas CPU pro jeden převod na alternativní datový typ (ten, který má vedlejší účinek odstranění časové části).

  • základ je výpočet odečítání od single rozdíl mezi dvěma vyvoláními:single - (round - single) . Je to orientační údaj, který předpokládá převod do az daného datového typu a datetime je přibližně stejný v obou směrech. Zdá se, že tento předpoklad není dokonalý, ale je blízko, protože všechny hodnoty jsou blízko 20 000 ms s jedinou výjimkou.

Ještě jedna zajímavá věc je, že základní cena se téměř rovná jedné Convert(date) metoda (což musí být téměř nulové, protože server dokáže interně extrahovat celočíselnou část dne přímo z prvních čtyř bajtů datetime datový typ).

Závěr

Takže to vypadá, že jednosměrný varchar metoda konverze trvá asi 1,8 μs a jednosměrná metoda DateDiff metoda trvá asi 0,18 μs. Vycházím z nejkonzervativnějšího času „základního procesoru“ při mém testování celkem 18 458 ms pro 25 920 000 řádků, takže 2 3218 ms / 25920 000 =0,18 μs. Zdánlivé 10násobné zlepšení se zdá být hodně, ale upřímně řečeno je to docela malé, dokud nebudete mít co do činění se stovkami tisíc řádků (617 tisíc řádků =úspora 1 sekundy).

I vzhledem k tomuto malému absolutnímu zlepšení, podle mého názoru, DateAdd metoda vítězí, protože je nejlepší kombinací výkonu a srozumitelnosti. Odpověď, která vyžaduje "magické číslo" 0.50000004 jednoho dne někoho kousne (pět nul nebo šest???), navíc je to těžší na pochopení.

Další poznámky

Až budu mít trochu času, změním 0.50000004 na '12:00:00.003' a uvidíte, jak to jde. Převede se na stejné datetime hodnotu a je pro mě mnohem snazší si ji zapamatovat.

Pro zájemce byly výše uvedené testy spuštěny na serveru, kde @@Version vrací následující:

Microsoft SQL Server 2008 (RTM) – 10.0.1600.22 (Intel X86) 9. července 2008 14:43:34 Copyright (c) 1988-2008 Microsoft Corporation Standard Edition v systému Windows NT 5.2 (sestavení 3790:Service Pack 2)



  1. Laravel:PDOException:nelze najít ovladač

  2. 7 strategií, jak ze schůzek vytěžit maximum

  3. Připojení Oracle k Amazon Aurora

  4. Jak vložit záznam pouze s výchozími hodnotami?