Каков наилучший способ усечения даты в SQL Server?

Если у меня есть значение даты, например 2010-03-01 17:34:12.018

Каков наиболее эффективный способ превратить это в 2010-03-01 00:00:00.000?

В качестве второстепенного вопроса, каков наилучший способ эмуляции функции Oracle TRUNC, которая позволит вам обрезать по годам, кварталам, месяцам, неделям, дням, часам, минутам и секундам?

Ответ 1

Чтобы округлить до ближайшего целого дня, существует три подхода в широком использовании. Первый использует datediff, чтобы найти количество дней со дня 0 datetime. 0 datetime соответствует 1 января 1900 года. Добавив разницу в день к дате начала, вы округлили до целого дня;

select dateadd(d, 0, datediff(d, 0, getdate()))

Второй метод основан на тексте: он обрезает текстовое описание с помощью varchar(10), оставляя только часть даты:

select convert(varchar(10),getdate(),111)

Третий метод использует тот факт, что a datetime действительно является плавающей точкой, представляющей число дней с 1900 года. Таким образом, округляя его до целого числа, например, используя floor, вы получаете начало дня

select cast(floor(cast(getdate() as float)) as datetime)

Чтобы ответить на второй вопрос, начало недели сложнее. Один из способов - вычесть день недели:

select dateadd(dd, 1 - datepart(dw, getdate()), getdate())

Это также возвращает часть времени, поэтому вам нужно будет объединить ее с одним из методов удаления времени, чтобы добраться до первой даты. Например, с @start_of_day в качестве переменной для удобочитаемости:

declare @start_of_day datetime
set @start_of_day = cast(floor(cast(getdate() as float)) as datetime)
select dateadd(dd, 1 - datepart(dw, @start_of_day), @start_of_day)

начало года, месяца, часа и минуты по-прежнему работает с подходом "разница с 1900 года":

select dateadd(yy, datediff(yy, 0, getdate()), 0)
select dateadd(m, datediff(m, 0, getdate()), 0)
select dateadd(hh, datediff(hh, 0, getdate()), 0)
select dateadd(mi, datediff(mi, 0, getdate()), 0)

Округление по секундам требует другого подхода, так как количество секунд с 0 дает переполнение. Один из способов - использовать начало дня вместо 1900 в качестве контрольной даты:

declare @start_of_day datetime
set @start_of_day = cast(floor(cast(getdate() as float)) as datetime)
select dateadd(s, datediff(s, @start_of_day, getdate()), @start_of_day)

Чтобы округлить до 5 минут, настройте метод округления минут. Возьмите частное от разницы минут, например, используя /5*5:

select dateadd(mi, datediff(mi,0,getdate())/5*5, 0)

Это работает и в кварталах и в полчаса.

Ответ 2

Если вы используете SQL Server 2008, вы можете использовать новый тип данных Date следующим образом:

select cast(getdate() as date)

Если вам все еще нужно, чтобы ваше значение было типом DateTime, вы можете сделать это:

select cast(cast(getdate() as date) as datetime)

Метод, который должен работать на всех версиях SQL Server:

select cast(floor(cast(getdate() as float)) as datetime)

Ответ 3

Try:

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

ОБНОВЛЕНИЕ: ответьте на второй вопрос: в течение многих лет вы могли бы использовать немного измененную версию моего ответа:

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

за квартал:

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

и т.д.

Я проверил, до минут - это нормально. Но в секундах у меня появилось сообщение о переполнении:

Разница двух столбцов datetime вызвало переполнение во время выполнения.

Еще одно обновление: взгляните на следующий ответ на тот же question

Ответ 4

Поздно, но вы получите точные результаты, запрошенные в сообщении. Я также чувствую, что это гораздо более интуитивно, чем использование dateadd, но это мои предпочтения.

declare @SomeDate datetime = '2010-03-01 17:34:12.018'
SELECT 
 DATEFROMPARTS(
     YEAR(@SomeDate)
    ,MONTH(@SomeDate)
    ,'01'
    ) AS CUR_DATE_FROM_PARTS
,DATETIMEFROMPARTS(
     YEAR(@SomeDate)                     
    ,MONTH(@SomeDate)                
    ,'01' --DAY(@SomeDate)                   
    ,'00' --DATEPART(HOUR,@SomeDate)         
    ,'00' --DATEPART(MINUTE,@SomeDate)       
    ,'00' --DATEPART(SECOND,@SomeDate)       
    ,'00' --DATEPART(MILLISECOND,@SomeDate) 
    ) AS CUR_DATETIME_FROM_PARTS
,@SomeDate                         AS CUR_DATETIME
,YEAR(@SomeDate)                   AS CUR_YEAR
,MONTH(@SomeDate)                  AS CUR_MONTH
,DAY(@SomeDate)                    AS CUR_DAY
,DATEPART(HOUR,@SomeDate)          AS CUR_HOUR
,DATEPART(MINUTE,@SomeDate)        AS CUR_MINUTE
,DATEPART(SECOND,@SomeDate)        AS CUR_SECOND
,DATEPART(MILLISECOND,@SomeDate)   AS CUR_MILLISECOND
FROM Your_Table

Усеченная дата: 2010-03-01

Усеченное время DateTime: 2010-03-01 00: 00: 00.000

DateTime: 2010-03-01 17: 34: 12.017