Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

Как да премахнете частта от времето на стойност за дата и час (SQL Server)?

SQL Server 2008 и по-нови версии

В SQL Server 2008 и по-нови, разбира се, най-бързият начин е Convert(date, @date) . Това може да бъде върнато към datetime или datetime2 ако е необходимо.

Какво наистина е най-доброто в SQL Server 2005 и по-стари?

Виждал съм непоследователни твърдения за това кое е най-бързото съкращаване на времето от дата в SQL Server и някои хора дори казаха, че са правили тестове, но моят опит е различен. Така че нека направим някои по-строги тестове и да оставим всички да имат скрипта, така че ако направя някакви грешки, хората могат да ме поправят.

Float преобразуванията не са точни

Първо, бих стоял далеч от конвертирането на datetime до float , защото не се преобразува правилно. Може да ви се размине да правите точно това премахване на времето, но мисля, че е лоша идея да го използвате, защото имплицитно съобщава на разработчиците, че това е безопасна операция и не . Разгледайте:

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

Това не е нещо, на което трябва да учим хората в нашия код или в нашите примери онлайн.

Освен това дори не е най-бързият начин!

Доказателство – Тестване на производителност

Ако искате сами да извършите някои тестове, за да видите как различните методи наистина се подреждат, тогава ще ви е необходим този скрипт за настройка, за да стартирате тестовете по-надолу:

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

Моля, имайте предвид, че това създава таблица от 427,57 MB във вашата база данни и ще отнеме около 15-30 минути за изпълнение. Ако вашата база данни е малка и е настроена на 10% растеж, това ще отнеме повече време, отколкото ако първо оразмерите достатъчно голям.

Сега за действителния скрипт за тестване на производителността. Моля, имайте предвид, че е целенасочено да не се връщат редове обратно на клиента, тъй като това е лудо скъпо за 26 милиона реда и би скрило разликите в производителността между методите.

Резултати от производителността

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;

Някои разбъркани анализи

Някои бележки за това. На първо място, ако просто извършвате GROUP BY или сравнение, няма нужда да конвертирате обратно в datetime . Така че можете да спестите малко CPU, като избягвате това, освен ако не се нуждаете от крайната стойност за целите на показване. Можете дори да GROUP BY неконвертираната стойност и да поставите преобразуването само в клаузата 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)

Също така вижте как цифровите преобразувания отнемат само малко повече време за преобразуване обратно в datetime , но varchar конверсията се удвоява почти? Това разкрива частта от процесора, която е предназначена за изчисляване на дата в заявките. Има части от използването на процесора, които не включват изчисляване на дата и това изглежда е нещо близо до 19875 ms в горните заявки. Тогава преобразуването отнема допълнителна сума, така че ако има две реализации, тази сума се изразходва приблизително два пъти.

Допълнително изследване разкрива, че в сравнение с Convert(, 112) , Convert(, 101) заявката има допълнителен разход на процесора (тъй като използва по-дълъг varchar ?), защото второто преобразуване обратно към date не струва толкова, колкото първоначалното преобразуване в varchar , но с Convert(, 112) тя е по-близо до същата базова цена на процесора от 20 000 ms.

Ето тези изчисления за времето на процесора, които използвах за горния анализ:

     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
  • кръгла е времето на процесора за двупосочно пътуване обратно до datetime .

  • единична е процесорното време за едно преобразуване към алтернативен тип данни (този, който има страничния ефект от премахването на частта от време).

  • база е изчислението на изваждане от single разликата между двете извиквания:single - (round - single) . Това е основна цифра, която предполага преобразуването към и от този тип данни и datetime е приблизително еднаква и в двете посоки. Изглежда, че това предположение не е идеално, но е близко, защото всички стойности са близо до 20 000 ms само с едно изключение.

Още едно интересно нещо е, че базовата цена е почти равна на единичния Convert(date) метод (който трябва да бъде почти 0 цена, тъй като сървърът може вътрешно да извлече целочислената дневна част направо от първите четири байта на datetime тип данни).

Заключение

Така че това, което изглежда е, че еднопосочният varchar методът на преобразуване отнема около 1,8 μs и еднопосочният DateDiff методът отнема около 0,18 μs. Базирам това на най-консервативното време на „базов процесор“ в моето тестване от общо 18458 ms за 25 920 000 реда, така че 23218 ms / 25920000 =0,18 μs. Очевидното подобрение от 10 пъти изглежда много, но честно казано е доста малко, докато не се справите със стотици хиляди редове (617 000 реда =1 секунда спестявания).

Дори предвид това малко абсолютно подобрение, според мен, DateAdd методът печели, защото е най-добрата комбинация от производителност и яснота. Отговорът, който изисква "магическо число" от 0.50000004 някой ден ще ухапе някого (пет нули или шест???), плюс това е по-трудно за разбиране.

Допълнителни бележки

Когато имам малко време, ще сменя 0.50000004 до '12:00:00.003' и вижте как става. Преобразува се в същия datetime стойност и намирам, че го запомням много по-лесно.

За тези, които се интересуват, горните тестове бяха извършени на сървър, където @@Version връща следното:

Microsoft SQL Server 2008 (RTM) - 10.0.1600.22 (Intel X86) 9 юли 2008 г. 14:43:34 Авторски права (c) 1988-2008 Microsoft Corporation Standard Edition на Windows NT 5.2 (Build 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. Как да проверя дали съществува ограничение в Sql сървър?

  2. Инструкция CASE WHEN за клауза ORDER BY

  3. SQL Server Fuzzy Search с процент на съвпадение

  4. Как мога да получа сумата от множество стойности за дата и час?

  5. можем ли да имаме външен ключ, който не е първичен ключ в никоя друга таблица?