Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Jak usunąć część czasu z wartości daty i godziny (SQL Server)?

SQL Server 2008 i nowsze

W SQL Server 2008 i nowszych oczywiście najszybszym sposobem jest Convert(date, @date) . Można to przywrócić do datetime lub datetime2 jeśli to konieczne.

Co jest naprawdę najlepsze w SQL Server 2005 i starszych?

Widziałem niespójne twierdzenia dotyczące tego, co jest najszybsze w skracaniu czasu od daty w SQL Server, a niektórzy nawet powiedzieli, że przeprowadzali testy, ale moje doświadczenie było inne. Zróbmy więc bardziej rygorystyczne testy i niech każdy ma skrypt, więc jeśli popełnię jakiś błąd, ludzie mogą mnie poprawić.

Konwersje zmiennoprzecinkowe nie są dokładne

Po pierwsze, trzymałbym się z daleka od konwertowania datetime do float , ponieważ nie konwertuje poprawnie. Możesz ujść na sucho z dokładnym usuwaniem czasu, ale myślę, że używanie go jest złym pomysłem, ponieważ domyślnie informuje programistów, że jest to bezpieczna operacja, a tak nie jest . Spójrz:

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

Nie jest to coś, czego powinniśmy uczyć ludzi w naszym kodzie lub w naszych przykładach online.

Poza tym nie jest to nawet najszybszy sposób!

Dowód — testowanie wydajności

Jeśli chcesz samodzielnie wykonać kilka testów, aby zobaczyć, jak różne metody naprawdę się stosują, będziesz potrzebować tego skryptu konfiguracyjnego, aby przeprowadzić testy w dół:

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

Pamiętaj, że spowoduje to utworzenie tabeli 427,57 MB w Twojej bazie danych, a jej uruchomienie zajmie około 15-30 minut. Jeśli twoja baza danych jest mała i ustawiona na 10% wzrost, zajmie to więcej czasu niż jeśli najpierw będziesz wystarczająco duży.

Teraz przejdźmy do skryptu testowania wydajności. Należy pamiętać, że celowe jest nie zwracanie wierszy klientowi, ponieważ jest to szalenie kosztowne w przypadku 26 milionów wierszy i ukryłoby różnice w wydajności między metodami.

Wyniki wydajności

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;

Niektóre analizy rambling

Kilka uwag na ten temat. Po pierwsze, jeśli wykonujesz tylko grupowanie według lub porównanie, nie ma potrzeby konwertowania z powrotem na datetime . Możesz więc zaoszczędzić trochę procesora, unikając tego, chyba że potrzebujesz końcowej wartości do celów wyświetlania. Możesz nawet GROUP BY nieprzekonwertowaną wartość i umieścić konwersję tylko w klauzuli 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)

Zobacz także, jak konwersje numeryczne zajmują tylko nieco więcej czasu, aby przekonwertować z powrotem na datetime , ale varchar konwersja prawie się podwaja? Pokazuje to, jaka część procesora jest przeznaczana na obliczanie dat w zapytaniach. Niektóre części użycia procesora nie wymagają obliczania dat i wydaje się, że w powyższych zapytaniach jest to coś zbliżonego do 19875 ms. Następnie konwersja wymaga dodatkowej kwoty, więc jeśli są dwie konwersje, ta kwota jest zużywana około dwa razy.

Więcej badań pokazuje, że w porównaniu z Convert(, 112) , Convert(, 101) zapytanie ma dodatkowy koszt procesora (ponieważ używa dłuższego varchar ?), ponieważ druga konwersja z powrotem do date nie kosztuje tyle, co początkowa konwersja do varchar , ale z Convert(, 112) jest bliższy temu samemu kosztowi bazowemu procesora 20000 ms.

Oto te obliczenia dotyczące czasu procesora, których użyłem do powyższej analizy:

     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
  • okrągły to czas procesora na podróż w obie strony z powrotem do datetime .

  • pojedynczy to czas procesora dla pojedynczej konwersji na alternatywny typ danych (ten, który ma efekt uboczny usunięcia części czasu).

  • podstawa to obliczenie odejmowania od single różnica między dwoma wywołaniami:single - (round - single) . Jest to liczba, która zakłada konwersję do iz tego typu danych oraz datetime jest mniej więcej taki sam w obu kierunkach. Wygląda na to, że to założenie nie jest idealne, ale jest bliskie, ponieważ wszystkie wartości są bliskie 20000 ms z jednym wyjątkiem.

Jeszcze jedną interesującą rzeczą jest to, że koszt bazowy jest prawie równy jednemu Convert(date) metoda (która musi kosztować prawie 0, ponieważ serwer może wewnętrznie wyodrębnić część dnia całkowitego z pierwszych czterech bajtów datetime typ danych).

Wniosek

Wygląda więc na to, że jednokierunkowy varchar metoda konwersji zajmuje około 1,8 μs, a jednokierunkowy DateDiff metoda trwa około 0,18 μs. Opieram to na najbardziej konserwatywnym czasie „podstawowego procesora” w moich testach, wynoszącym łącznie 18458 ms dla 25 920 000 wierszy, więc 23218 ms / 25920000 =0,18 μs. Pozorna 10-krotna poprawa wydaje się być duża, ale szczerze mówiąc jest dość mała, dopóki nie masz do czynienia z setkami tysięcy wierszy (617 tys. wierszy =1 sekunda oszczędności).

Nawet biorąc pod uwagę tę niewielką absolutną poprawę, moim zdaniem, DateAdd Metoda wygrywa, ponieważ jest najlepszą kombinacją wydajności i przejrzystości. Odpowiedź, która wymaga „magicznej liczby” 0.50000004 kiedyś kogoś ugryzie (pięć zer czy sześć???), a do tego trudniej to zrozumieć.

Dodatkowe uwagi

Kiedy będę miał trochę czasu, zmienię 0.50000004 do '12:00:00.003' i zobacz, jak to działa. Jest konwertowany na ten sam datetime wartość i jest mi o wiele łatwiej ją zapamiętać.

Dla zainteresowanych powyższe testy zostały przeprowadzone na serwerze, na którym @@Version zwraca następujące dane:

Microsoft SQL Server 2008 (RTM) — 10.0.1600.22 (Intel X86) 9 lipca 2008 14:43:34 Prawa autorskie (c) 1988-2008 Microsoft Corporation Standard Edition w systemie Windows NT 5.2 (kompilacja 3790:Service Pack 2)



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Nie można otworzyć bazy danych, ponieważ jest to wersja 782. Ten serwer obsługuje wersję 706 i wcześniejsze. Ścieżka zmiany wersji nie jest obsługiwana

  2. Przekaż Dictionary<string,int> do procedury składowanej T-SQL

  3. Wstawianie wielu wierszy w jednym zapytaniu SQL?

  4. SQL Server DELETE — usuwanie jednego lub więcej wierszy z tabeli w nieco zaawansowanych scenariuszach

  5. Łączenie się z SQL Server 2012 przy użyciu sqlalchemy i pyodbc