Каков наилучший способ обрезать дату в SQL Server? - PullRequest
13 голосов
/ 14 апреля 2010

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

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

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

Ответы [ 4 ]

31 голосов
/ 14 апреля 2010

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

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

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

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

Третий метод использует тот факт, что 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)

Это работает и на четверть с половиной часа.

15 голосов
/ 14 апреля 2010

Если вы используете 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)
2 голосов
/ 05 мая 2017

Это поздно, но даст точные результаты, запрошенные в посте. Я также чувствую, что это намного более интуитивно понятно, чем использование 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

Сокращенное время: 2010-03-01 00: 00: 00.000

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

2 голосов
/ 14 апреля 2010

Попробуйте:

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

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

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

за квартал:

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

и т. Д.

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...