Denis Gladkikh

outcoldman

My personal blog about software development

Этот текст является в какой то мере переводом топика Kevin Jones - Removing time from SQL datetime, так что если вы хорошо знаете английский, то лучше, наверное, читать руководство из первых рук. Правда, мой вариант дополнен некоторыми тестами.

Итак, мы довольно часто используем SQL сервер для хранения данных с типом дата и время. В SQL Server 2005/2000 существуют два типа данных (специальных типов данных) для хранения даты и времени – это datetime и smalldatetime, разница между ними в возможностях хранения (от и до), точности времени и, соответственно, в количестве используемой памяти. В SQL Server 2008 появились дополнительные типы данных, такие как datetime2, time, date, datetimeoffset, о них вы можете прочитать в статье на MSDN - Типы данных и функции даты и времени (Transact-SQL).

Вернемся же к типу DATETIME. Часто возникает необходимость выбрать из типа DATETIME только дату, а время установить равным 0:00. Не приходиться об этом думать, когда нужно просто вывести результат – тогда все можно сделать форматом вывода, например в C# это может быть “dd.MM.yyyy”. Другое дело, если с данными нужно еще оперировать (например, группировать по дате или что то прибавить или убавить), тогда нам необходим тип DATETIME, в котором нам нужно обнулять время.

Первый вариант, как можно это сделать (до прочтения топика Kevin Jones’а я так всегда и делал) – это привести изначально тип DATETIME в VARCHAR без времени (определенным форматом) и обратно:

SELECT CONVERT(DATETIME, CONVERT(VARCHAR(15), GETDATE(), 101))

Данный вариант достаточно часто встречается. Правда, от него могут быть проблемы в производительности, когда вы будите обрабатывать несколько тысяч строк и более.

Другой вариант, если вы используете SQL Server 2008 – это приводить DATETIME к упомянутому выше типу DATE:

SELECT CAST(GETDATE() AS DATE)

Вы так же можете привести данный тип потом к DATETIME, если вам необходимо оперировать именно с этим типом.

И все же, если вы до сих пор используете SQL Server версии 2005, то лучше способ, чем описанный выше способ с VARCHAR – это приведение к типу FLOAT, вызов FLOOR (целое от числа), а затем приведение обратно к DATETIME:

SELECT CAST(FLOOR(CAST(GETDATE() AS FLOAT)) AS DATETIME)

Kevin Jones утверждает, что данные операции пройдут быстрее, аргументируя это тем, что во время приведения к типу VARCHAR и обратно SQL сервер еще задумывается о collation и о форматах. Когда же переводишь тип данных DATETIME к FLOAT, то целая часть числа – хранит информацию о дне, а дробная о времени. Использую функцию FLOAR мы берем только время. Кстати, с таким подходом легко, к примеру, сразу же прибавить день к дате.

Я в отличие от Kevin Jones попробовал все таки провести тест сравнения этих двух методов, правда использовав SQL Server 2008. Я написал следующий тест:

set nocount on 
go 
 
declare @date datetime, @i int 
  
declare @test table(d datetime) 
 
set @date = getdate() 
set @i = 0 
 
-- заполняем тестовыми данными (10000001 строка с датами) 
while @i <= 10000000 
begin  
    insert into @test (d)  
    values (@date) 
 
    set @date = dateadd(minute, 1, @date) 
    set @i = @i + 1 
end 
 
 
print convert(varchar(20), getdate(), 109) 
 
-- тестируем способ с VARCHAR 
select convert(datetime, convert(varchar(15), d, 101))  
from @test 
 
print convert(varchar(20), getdate(), 109) 
 
-- тестируем способ с FLOAT 
select cast(floor(cast(d as float)) as datetime) 
from @test 
 
print convert(varchar(20), getdate(), 109)

Результат выполнения (в окне Messages):

Aug 10 2009 11:36:07
Aug 10 2009 11:37:15
Aug 10 2009 11:38:20

То есть, разница всего то в 3-х секундах (Если поменять местами методы, то разницы вообще не будет). Итог: при еще большем объеме, может быть, это и сыграет роль, но в данном случае выигрыш не заметен. Потому вывод: данный способ (приведения к FLOAT и обратно) нужно взять на заметку и использовать в дальнейшем, но в данный момент не стоит переписывать существующий функционал, так как большого прироста производительности это не даст.

P.S. Может быть, в SQL Server 2000/2005 будут другие результаты? Или, может быть, в реальной жизни выигрыш будет?

UPDATE

Благодаря комментариям Ulugbek Umirov и Евгений Веприков из ветки блогов GotDotNet получены более честные результаты.

Было предложено использовать COUNT, а не чистый вывод в окно Managment Studio:

print convert(varchar(20), getdate(), 109) 
 
-- тестируем способ с VARCHAR 
select count(convert(datetime, convert(varchar(15), d, 101))) 
from @test 
 
print convert(varchar(20), getdate(), 109) 
 
-- тестируем способ с FLOAT 
select count(cast(floor(cast(d as float)) as datetime)) 
from @test 
 
print convert(varchar(20), getdate(), 109)

И тогда результаты будут уже более значимыми:

Aug 11 2009 12:14:30
Aug 11 2009 12:14:38
Aug 11 2009 12:14:41

Разница уже более чем в два раза.

Так же был предложен еще один метод by Ulugbek Umirov:

SELECT DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE())) 

Данный метод показывает лучше результаты на 2000 сервере, на 2005/2008 почти такой же.

Comments