SQL Server 2008 и выше
В SQL Server 2008 и выше, конечно, самый быстрый способ - Convert(date, @date)
. Это может быть приведено к datetime
или datetime2
при необходимости.
Что действительно лучше в SQL Server 2005 и старше?
Я видел непоследовательные утверждения о том, что быстрее всего урезать время с даты в SQL Server, и некоторые люди даже говорили, что проводили тестирование, но мой опыт был другим. Итак, давайте проведем еще более жесткое тестирование и дадим всем сценарий, поэтому, если я сделаю какие-либо ошибки, люди могут исправить меня.
преобразования с плавающей точкой не точны
Во-первых, я бы не стал конвертировать 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 МБ, и ее запуск займет около 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;
Некоторый анализ Rambling
Несколько замечаний по этому поводу. Прежде всего, если просто выполнить GROUP BY или сравнение, нет необходимости преобразовывать обратно в datetime
. Таким образом, вы можете сэкономить некоторый процессор, избегая этого, если только вам не нужно окончательное значение для отображения. Вы можете даже 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 мс. Тогда для преобразования требуется некоторая дополнительная сумма, поэтому, если есть две конверсии, эта сумма расходуется примерно вдвое.
Дополнительные исследования показывают, что по сравнению с Convert(, 112)
запрос Convert(, 101)
требует дополнительных затрат ресурсов ЦП (поскольку он использует более длинный varchar
?), Поскольку второе преобразование обратно в date
не стоит так дорого как первоначальное преобразование в varchar
, но с Convert(, 112)
оно ближе к той же базовой стоимости процессора в 20000 мс.
Вот расчеты времени ЦП, которые я использовал для анализа выше:
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
round - время ЦП для возврата в оба конца до datetime
.
single - это время ЦП для однократного преобразования в альтернативный тип данных (тот, который имеет побочный эффект удаления временной части).
base - вычисление вычитания из single
разницы между двумя вызовами: single - (round - single)
. Это приблизительная цифра, которая предполагает преобразование в и из этого типа данных, и datetime
примерно одинаково в обоих направлениях. Похоже, это предположение не является идеальным, но оно близко, потому что все значения близки к 20000 мс с одним исключением.
Еще одна интересная вещь заключается в том, что базовая стоимость почти равна одному методу Convert(date)
(который должен составлять почти 0 затрат, поскольку сервер может внутренне извлекать целочисленную часть дня прямо из первых четырех байтов datetime
тип данных).
Заключение
То, на что это похоже, заключается в том, что метод преобразования varchar
в одном направлении занимает около 1,8 мкс, а метод DateDiff
в одном направлении - около 0,18 мкс. Я основываюсь на самом консервативном времени «базового процессора» в моем тестировании: 18458 мс для 25 920 000 строк, поэтому 23218 мс / 25920000 = 0,18 мкс. Кажущееся 10-кратное улучшение кажется значительным, но оно откровенно довольно мало, пока вы не разберетесь с сотнями тысяч строк (617 тыс. Строк = экономия за 1 секунду).
Даже с учетом этого небольшого абсолютного улучшения, на мой взгляд, метод DateAdd
выигрывает, поскольку он является наилучшим сочетанием производительности и четкости. Ответ, требующий "магического числа" 0.50000004
, когда-нибудь кого-нибудь укусит (пять нулей или шесть ???), плюс его сложнее понять.
Дополнительные примечания
Когда у меня будет некоторое время, я собираюсь изменить 0.50000004
на '12:00:00.003'
и посмотреть, как это происходит. Он конвертируется в то же значение datetime
, и мне гораздо легче его запомнить.
Для тех, кто заинтересован, вышеприведенные тесты были запущены на сервере, где @@ Version возвращает следующее:
Microsoft SQL Server 2008 (окончательная первоначальная версия) - 10.0.1600.22 (Intel X86) 9 июля 2008 г. 14:43:34 Copyright (c) 1988-2008 Microsoft Corporation Standard Edition для Windows NT 5.2 (сборка 3790: пакет обновления 2)