Как я могу обрезать дату и время в SQL Server?

281

Каков наилучший способ обрезать значение даты и времени (чтобы удалить часы, минуты и секунды) в SQL Server 2008?

Например:

declare @SomeDate datetime = '2009-05-28 16:30:22'
select trunc_date(@SomeDate)

-----------------------
2009-05-28 00:00:00.000
Хулио Сезар
источник

Ответы:

495

Это продолжает часто собирать дополнительные голоса, даже спустя несколько лет, и поэтому мне нужно обновить его для современных версий Sql Server. Для Sql Server 2008 и более поздних версий это просто:

cast(getDate() As Date)

Обратите внимание, что последние три абзаца в нижней части все еще применяются, и вам часто нужно сделать шаг назад и найти способ избежать броска в первую очередь.

Но есть и другие способы сделать это. Вот самые распространенные.

Правильный путь (новый с Sql Server 2008):

cast(getdate() As Date)

Правильный путь (старый):

dateadd(dd, datediff(dd,0, getDate()), 0)

Сейчас он старше, но все же стоит знать, потому что он также может легко адаптироваться к другим временным точкам, таким как первый момент месяца, минуты, часа или года.

Этот правильный путь использует документированные функции, которые являются частью стандарта ANSI и гарантированно работают, но это может быть несколько медленнее. Он работает, определяя, сколько дней существует с 0 до текущего дня, и добавляя это количество дней к 0. Это будет работать независимо от того, как хранится ваша дата и время, и независимо от вашей локали.

Быстрый путь:

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

Это работает, потому что столбцы даты и времени хранятся в виде 8-байтовых двоичных значений. Приведите их к плавающему, напишите их, чтобы удалить дробь, и временная часть значений исчезнет, ​​когда вы приведете их обратно к datetime. Это все немного сдвигается без сложной логики, и это очень быстро.

Помните, что это зависит от деталей реализации, которые Microsoft может изменить в любое время, даже при автоматическом обновлении службы. Это также не очень портативный. На практике очень маловероятно, что эта реализация изменится в ближайшее время, но все же важно осознавать опасность, если вы решите ее использовать. И теперь, когда у нас есть возможность разыграть дату, это редко необходимо.

Неправильный путь:

cast(convert(char(11), getdate(), 113) as datetime)

Неправильный способ работает путем преобразования в строку, усечения строки и преобразования обратно в дату и время. Это неправильно по двум причинам: 1) это может не работать во всех локалях и 2) это самый медленный из возможных способов сделать это ... и не только немного; это на порядок или два медленнее, чем другие варианты.


Обновление В последнее время я получал несколько голосов, и поэтому я хочу добавить, что с момента публикации я увидел довольно веские доказательства того, что Sql Server оптимизирует разницу в производительности между «правильным» и «быстрым» способами. Это означает, что вы должны отдать предпочтение первым.

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

В большинстве мест база данных уже является вашим узким местом. Обычно это сервер, который является самым дорогим для добавления оборудования для повышения производительности, и самый сложный для правильной установки этих дополнений (например, необходимо балансировать диски с памятью). Кроме того, его сложнее масштабировать как с технической точки зрения, так и с точки зрения бизнеса; Технически гораздо проще добавить веб-сервер или сервер приложений, чем сервер базы данных, и даже если это неверно, вы не платите $ 20 000 за серверную лицензию для IIS или apache.

Суть, которую я пытаюсь подчеркнуть, заключается в том, что по возможности вы должны выполнять эту работу на уровне приложений. Только раз , когда вы когда - нибудь найти себе усечения DateTime на Sql Server, когда вам нужно сгруппировать в день, и даже тогда , вероятно , вы должны иметь дополнительный набор столбцов в качестве вычисляемого столбца, поддерживается во время вставки / обновления или техническое обслуживание в логике приложения. Получите эту тяжелую работу с вашими процессорами из вашей базы данных.

Джоэл Коухорн
источник
6
«быстрый путь» по-прежнему остается самым быстрым путем для sql 2008, согласно тесту, который я только что выполнил
Сэм Саффрон
3
К вашему сведению: stackoverflow.com/q/1177449/27535 и stackoverflow.com/q/133081/27535 dateadd / datediff "выигрывает ...". Для единственной переменной, которая заботится, конечно, и можно надеяться, что вы вычислили столбцы или что-то вроде более миллиона строк :-)
gbn
9
Этот «правильный» способ только случайно работает. Это написано так, как если бы синтаксис для DateAdd был (интервал, дата, приращение), но это не так. Это (интервал, приращение, дата). Я наткнулся на это, когда попытался урезать дату до первого месяца: SELECT DATEADD (m, 0, DATEDIFF (m, 0, GETDATE ())) не работает, но SELECT DATEADD (m, DATEDIFF (m, 0, GETDATE ()), 0) делает. По крайней мере, это то, что я вижу в 2008R2.
Келли Клайн
1
@ Келли в 2008R2, почему бы не просто cast(getdate() as date)?
Джоэл Coehoorn
2
Они все работают на колонке даты и времени. getdate()Вот замена для любого источника даты и времени, который у вас может быть.
Джоэл Кохорн
44

Только для SQL Server 2008

CAST(@SomeDateTime AS Date) 

Затем приведите его к дате и времени, если хотите

CAST(CAST(@SomeDateTime AS Date) As datetime)
DJ.
источник
Хороший вопрос: я все еще в 2005 году, и поэтому в 2008 году это, вероятно, новый «правильный» путь, который может даже соответствовать производительности «быстрого» пути.
Джоэл Коухорн
1
Производительность этого нового способа даже быстрее, чем «быстрый» способ.
ErikE
21

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

Это отличается от принятого ответа тем, что вы можете использовать не только dd(дни), но и любую из частей даты (см. Здесь ):

dateadd(minute, datediff(minute, 0, GETDATE()), 0)

Обратите внимание, что в приведенном выше выражении 0константная дата начала года (1900-01-01). Если вам нужно усечь меньшие части, например, секунды или миллисекунды, вам нужно взять постоянную дату, которая ближе к дате, которая будет усечена, чтобы избежать переполнения.

Лусеро
источник
1
Это было чудовищно полезно. Я искал способ обрезать дату и время в месте, которое меньше полного дня.
Майкл - Где Клэй Ширки
1
@ Майкл, спасибо за отзыв, приятно знать, что он тебе помог!
Лусеро
1
+1 это должно иметь больше голосов, это отличный ответ, который расширяет выбранный ответ.
jtate
1
Интернет знает, что вам не нужно ограничиваться полными датами. Вот пример для 15-минутных интервалов с использованием целочисленного деления:dateadd(minute, datediff(minute, 0, GETDATE()) / 15 * 15, 0)
Майкл - Где Клэй Ширки
7

Фрагмент, который я нашел в Интернете, когда мне нужно было сделать это:

 dateadd(dd,0, datediff(dd,0, YOURDATE))
 e.g.
 dateadd(dd,0, datediff(dd,0, getDate()))
Том Риттер
источник
Я нахожусь на 2005, но я думал, что у 2008 была новая функция для этого ??
КМ.
2
Ухоженная! Я бы прибегнул к разделению дат и использованию обработки строк для их объединения. Может быть неактуально, но SQL2008 имеет тип данных только для даты без элемента времени.
Франс
1
И обратите внимание, у вас есть смешанные операнды DateAdd DateAdd(dd, DateDiff(...), 0). Это может укусить вас, если вы не будете осторожны.
Эрик
1

В SQl 2005 ваша функция trunc_date может быть написана так.

(1)

CREATE FUNCTION trunc_date(@date DATETIME)
RETURNS DATETIME
AS
BEGIN
    CAST(FLOOR( CAST( @date AS FLOAT ) )AS DATETIME)
END

Первый способ намного чище. Он использует только 3 вызова метода, включая финальный CAST (), и не выполняет конкатенацию строк, что является автоматическим плюсом. Кроме того, здесь нет огромных типов бросков. Если вы можете себе представить, что могут быть представлены метки даты / времени, то преобразование дат в числа и обратно в даты является довольно простым процессом.

(2)

CREATE FUNCTION trunc_date(@date DATETIME)
RETURNS DATETIME
AS
BEGIN
      SELECT CONVERT(varchar, @date,112)
END

Если вы обеспокоены реализацией в Microsoft datetime (2) или (3), может быть в порядке.

(3)

CREATE FUNCTION trunc_date(@date DATETIME)
RETURNS DATETIME
AS
BEGIN
SELECT CAST((STR( YEAR( @date ) ) + '/' +STR( MONTH( @date ) ) + '/' +STR( DAY(@date ) )
) AS DATETIME
END

В-третьих, более многословный метод. Для этого необходимо разбить дату на части года, месяца и дня, собрать их в формате «гггг / мм / дд», а затем привести их обратно к дате. Этот метод включает 7 вызовов метода, включая заключительный CAST (), не говоря уже о конкатенации строк.

AlejandroR
источник
1
CONVERT(DATE, <yourdatetime>) or CONVERT(DATE, GetDate()) or CONVERT(DATE, CURRENT_TIMESTAMP)
декан
источник
0

Для тех из вас, кто пришел сюда и ищет способ обрезать поле DATETIME до значения, меньшего целого дня, например каждую минуту, вы можете использовать это:

SELECT CAST(FLOOR(CAST(GETDATE() AS FLOAT)) + (FLOOR((CAST(GETDATE() AS FLOAT) - FLOOR(CAST(GETDATE() AS FLOAT))) * 1440.0) + (3.0/86400000.0)) / 1440.0 AS DATETIME)

так что если бы сегодня было 2010-11-26 14:54:43.123то это вернулось бы 2010-11-26 14:54:00.000.

Чтобы изменить интервал, к которому он переходит, замените 1440.0 на число интервалов в дне, например:

24hrs          =   24.0  (for every hour)
24hrs / 0.5hrs =   48.0  (for every half hour)
24hrs / (1/60) = 1440.0  (for every minute)

(Всегда ставьте .0на конец, чтобы неявно бросить на поплавок.)


Для тех из вас , интересно , что (3.0/86400000)это и в моем расчете, SQL Server 2005 , кажется, не гипс от FLOATк DATETIMEточно, так что это добавляет 3 миллисекунды до того пола его.

BG100
источник
1
Будьте осторожны с ошибками округления из-за пределов точности с плавающей запятой, хотя ... также, это не работает с datetime2типом данных.
Лусеро
Для Hour также работает SELECT DATEADD (hour, DATEDIFF (hour, 0, GETDATE ()), 0). Минуту тоже, но Second приведет к переполнению.
Келли Клайн
Кастинг, чтобы плавать и вернуться к дате и времени не работает правильно .
Эрик
0

Этот запрос должен дать вам результат, эквивалентный trunc(sysdate)в Oracle.

SELECT  * 
FROM    your_table
WHERE   CONVERT(varchar(12), your_column_name, 101)
      = CONVERT(varchar(12), GETDATE(), 101)

Надеюсь это поможет!

Сандип Гааде
источник
0

Вы также можете извлечь дату using Substringиз переменной datetime, а приведение к datetime будет игнорировать часть времени.

declare @SomeDate datetime = '2009-05-28 16:30:22'
SELECT cast(substring(convert(varchar(12),@SomeDate,111),0,12) as Datetime) 

Кроме того, вы можете получить доступ к частям переменной datetime и объединить их в усеченную дату конструкции, что-то вроде этого:

SELECT cast(DATENAME(year, @Somedate) + '-' + 
       Convert(varchar(2),DATEPART(month, @Somedate)) + '-' +
       DATENAME(day, @Somedate) 
       as datetime)
NeverHopeless
источник
0

Oracle:

TRUNC(SYSDATE, 'MONTH')

SQL Server:

DATEADD(DAY, - DATEPART(DAY, DateField) + 1, DateField)

Аналогичным образом может использоваться для сокращения минут или часов от даты.

Markus
источник
0

Вы можете просто сделать это (SQL 2008):

объявлять @SomeDate date = getdate ()

select @SomeDate

2009-05-28

Хагай Даненберг-Лернер
источник