Як я можу обрізати дату в 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-байтних бінарних значень. Закиньте їх плаваючими, підлогу, щоб видалити дріб, і часова частина значень пішла, коли ви повертаєте їх до дату. Це все просто біт зміщення без складної логіки, і це дуже швидко.

Зауважте, що це покладається на деталі впровадження, які Microsoft може вільно змінити в будь-який час, навіть в автоматичному оновленні служби Це також не дуже портативно. На практиці дуже малоймовірно, що ця реалізація незабаром зміниться, але все ж важливо усвідомлювати небезпеку, якщо ви вирішите її використовувати. А тепер, коли у нас є можливість зняти участь як побачення, це рідко буває необхідно.

Невірний шлях:

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

Неправильний спосіб працює шляхом перетворення в рядок, обрізання рядка та перетворення назад у дату. Це неправильно з двох причин: 1) це може працювати не у всіх локалях, і 2) мова йде про найповільніший можливий спосіб зробити це ... і не лише трохи; це як на порядок або на два повільніше, ніж інші варіанти.


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

У будь-якому випадку ви хочете написати свої запити, щоб уникнути необхідності робити це в першу чергу . Дуже рідко слід виконувати цю роботу над базою даних.

У більшості місць база даних вже є вашим вузьким місцем. Як правило, це найдорожчий сервер для додавання апаратного забезпечення для покращення продуктивності, а найскладніший для правильного встановлення цих доповнень (наприклад, ви повинні врівноважити диски з пам'яттю). Це також найважче масштабувати зовні, як технічно, так і з точки зору бізнесу; технічно набагато простіше додати веб-сервер або сервер додатків, ніж сервер баз даних, і навіть якщо це було помилково, ви не платите $ 20000 + за ліцензію сервера за IIS або apache.

Я намагаюся зробити так, що коли це можливо, ви повинні виконувати цю роботу на рівні програми. Тільки раз , коли ви коли - небудь знайти собі усічення DateTime на Sql Server, коли вам потрібно згрупувати в день, і навіть тоді , ймовірно , ви повинні мати додатковий набір стовпців як обчислюється стовпчика, підтримується під час вставки / оновлення або технічне обслуговування в логіці програми. Отримайте цю роботу з індексами, важкі для процесора ваші бази даних.


6
"швидкий шлях" - це все-таки найшвидший шлях для sql 2008 року за базовим показником, який я щойно провів
Сем Сафрон

3
FYI: stackoverflow.com/q/1177449/27535 і stackoverflow.com/q/133081/27535 DateAdd / DATEDIFF "перемагає ...". Для однієї змінної, якій важливо, звичайно, і ви сподіваєтесь, що ви обчислили стовпчики або такі більше мільйона рядків :-)
gbn

9
Цей "правильний" спосіб працює лише випадково. Написано так, ніби синтаксис для DateAdd (інтервал, дата, приріст), але це не так. Це (інтервал, приріст, дата). Я наткнувся на це, коли спробував урізати дату до першого місяця: ВИБІР DATEADD (m, 0, DATEDIFF (m, 0, GETDATE ())) не працює, але ВИБІР DATEADD (m, DATEDIFF (m, 0, GETDATE ()), 0). Принаймні, це я бачу у 2008R2.
Келлі Клайн

1
@Kelly у 2008R2, чому б не просто cast(getdate() as date)?
Joel Coehoorn

2
Вони все працюють на колонці дати і часу. getdate()Тут ви знайдете резервне джерело для будь-якого джерела дати.
Joel Coehoorn

44

Лише для SQL Server 2008

CAST(@SomeDateTime AS Date) 

Потім поверніть його до дату, якщо хочете

CAST(CAST(@SomeDateTime AS Date) As datetime)

Хороший момент: я все ще на 2005 рік, і тому для 2008 року це, мабуть, новий «правильний» спосіб і навіть може відповідати продуктивності «швидкого» шляху.
Джоел Куехорн

1
Виконання цього нового способу навіть швидше, ніж "швидкий" шлях.
ЕрікЕ

21

Просто заради більш повної відповіді, ось робочий спосіб обрізання будь-якої з частин дати вниз, включаючи хвилини (замінити GETDATE()датою для врізання).

Це відрізняється від прийнятої відповіді тим, що ви можете використовувати не тільки dd(дні), а й будь-яку частину дати (див. Тут ):

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

Зауважте, що у вислові вище значення 0є постійною датою на початок року (1900-01-01). Якщо вам потрібно скоротити до менших частин, наприклад, до секунд або мілісекунд, вам потрібно взяти постійну дату, яка ближче до дати, яка буде усічена, щоб уникнути переповнення.


1
Це було жахливо. Я шукав у всьому шляху спосіб урізати дату в місці, нижчому за повний день.
Майкл - Де Клей Ширкий

1
@Michael, дякую за відгук, добре знати, що це допомогло тобі!
Lucero

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). Це може вкусити вас, якщо ви не будете обережні.
ErikE

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

Якщо ви стурбовані впровадженням даними (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 (), не кажучи вже про об'єднання рядків.


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 мілісекунди до того статі його.


1
Будьте обережні з помилками округлення через межі точності з плаваючою комою ... хоча це також не працює з datetime2типом даних.
Lucero

Для години працює також SELECT DATEADD (hour, DATEDIFF (hour, 0, GETDATE ()), 0). Хвилина теж, але Друга призведе до переповнення.
Келлі Клайн

Передача в плаваючий і назад у поточний час працює неправильно .
ErikE

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)

0

Oracle:

TRUNC(SYSDATE, 'MONTH')

SQL Server:

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

Можна аналогічно використовувати для обрізання хвилин або годин від дати.



Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.