DateTime2 против DateTime в SQL Server

762

Который из:

это рекомендуемый способ даты и времени хранения в SQL Server 2008+?

Я знаю о различиях в точности (и, возможно, о размере места для хранения), но игнорируя их на данный момент, есть ли документ с рекомендациями о том, когда что использовать, или, может быть, нам следует использовать datetime2только одно ?

Mikeon
источник

Ответы:

641

В документации MSDN для datetime рекомендуется использовать datetime2 . Вот их рекомендация:

Используйте time, date, datetime2и datetimeoffsetтип данных для новой работы. Эти типы соответствуют стандарту SQL. Они более портативны. time, datetime2И datetimeoffset обеспечивают более секунды точности. datetimeoffsetобеспечивает поддержку часовых поясов для глобально развернутых приложений.

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

Адам Порад
источник
46
хотя с datetime2 повышена точность, некоторые клиенты не поддерживают date, time или datetime2 и вынуждают вас преобразовывать в строковый литерал. Если вас больше беспокоит совместимость, чем точность, используйте datetime
FistOfFury
4
Другим вариантом является использование индексированного представления со столбцом, преобразованным как дата-время для совместимости. Тем не менее, вы должны быть в состоянии указать приложение на представление.
TamusJRoyce
9
Поддержка часовых поясов с DATETIMEOFFSET является неправильным. Он сохраняет только смещение UTC для определенного момента времени, а не часового пояса.
Suncat2000
6
@Porad: В чем именно преимущество на практике «быть более переносимым» из-за того, что он является «стандартом SQL»? Это помимо того, что вы пишете значительно больше кода, который значительно менее читабелен / удобен для «порта» в другую СУБД, которая вероятно, никогда не произойдет в течение срока действия этого кода. Кроме, возможно, предоставленных Microsoft инструментов и драйверов SQL Server (если даже), есть ли приложения, которые на самом деле полагаются на конкретные представления типа Bit DateTime2(или любого другого SQL Server) Типа для этого)? См. Минусы в моем ответе 7/10/17 ниже, почему я спрашиваю.
Том
2
@ Adam Porad: Кроме того, все эти преимущества, скорее всего, не нужны (за исключением инженерных или научных приложений) и, следовательно, не стоят больших потерь, НАМНОГО более вероятно: гораздо более легкая (даже с учетом обходных путей) возможность неявного / явного преобразования в числовое значение с плавающей точкой (число дней, включая, если применимо, дробные дни с минимальной даты-времени) для сложений, вычитаний, минимумов, максимумов и средних значений. См. Минусы в моем ответе 7/10/17 ниже для деталей.
Том
493

DATETIME2имеет диапазон дат от « DATETIME0001/01/01» до «9999/12/31 », в то время как тип поддерживает только год 1753-9999.

Кроме того, если вам нужно, DATETIME2может быть более точным с точки зрения времени; DATETIME ограничен 3 1/3 миллисекундами, но DATETIME2может быть точным до 100 нс.

Оба типа отображаются System.DateTimeв .NET - никакой разницы нет.

Если у вас есть выбор, я бы порекомендовал использовать по DATETIME2возможности. Я не вижу каких-либо преимуществ DATETIME(за исключением обратной совместимости) - у вас будет меньше проблем (с датами, выходящими за пределы допустимого диапазона и возникающими из-за этого).

Плюс: если вам нужна только дата (без временной части), используйте DATE - это так же хорошо, как DATETIME2и экономит ваше место! :-) То же самое касается только времени - используйте TIME. Вот для чего существуют эти типы!

marc_s
источник
158
Будьте осторожны при добавлении значения .NET DateTime в качестве параметра к SqlCommand, потому что ему нравится предполагать, что это старый тип datetime, и вы получите сообщение об ошибке, если попытаетесь записать значение DateTime, выходящее за пределы 1753-9999 года. если вы явно не укажете тип как System.Data.SqlDbType.DateTime2 для SqlParameter. В любом случае datetime2 - это здорово, потому что он может хранить любое значение, которое может быть сохранено в типе .NET DateTime.
Трийнко
10
@marc_s - разве это не то, для чего null?
JohnFx
8
@JohnFX - здесь немного поздно - но вы бы не установили дату и время на ноль. Вы бы использовали Nullable <datetime> или datetime? который обрабатывает ноль просто отлично - и при сопоставлении с процедурой просто сделать param.value = someDateTime ?? DBValue.Null Его несчастная мы застряли с типом данных с номером после - только кажется , так что «общий»:)
Adam Tuliper - MSFT
69
Lol, я просто попытался поднять свой собственный комментарий (выше), прежде чем я понял, что это был мой собственный комментарий (сделанный более года назад). Я все еще имею дело с глупым решением разработки .NET Framework TRUNCATE для всех значений DateTime по умолчанию, когда они передаются как SqlParameters, если вы явно не установите его в более точный SqlDbType.DateTime2. Так много для автоматического вывода правильного типа. На самом деле, они должны были сделать изменение прозрачным, заменив менее точную, менее эффективную реализацию с ограниченным диапазоном и сохранив исходное имя типа «дата-время». См. Также stackoverflow.com/q/8421332/88409
Triynko
5
@marc_s Разве не Nullable<DateTime>для этого?
ChrisW
207

datetime2 выигрывает в большинстве аспектов, кроме (совместимость старых приложений)

  1. больший диапазон значений
  2. лучшая точность
  3. меньший объем памяти (если указана необязательная точность, указанная пользователем)

SQL Сравнение типов данных даты и времени - дата-время, дата-время2, дата, время

пожалуйста, обратите внимание на следующие моменты

  • Синтаксис
    • datetime2 [(точность долей секунды => выглядит ниже размера хранилища)]
  • Точность, масштаб
    • От 0 до 7 цифр с точностью до 100 нс.
    • Точность по умолчанию составляет 7 цифр.
  • Размер хранилища
    • 6 байтов для точности менее 3;
    • 7 байтов для точности 3 и 4.
    • Вся остальная точность требует 8 байтов .
  • DateTime2 (3) имеет то же количество цифр, что и DateTime, но использует 7 байтов памяти вместо 8 байтов ( SQLHINTS-DateTime против DateTime2 )
  • Узнайте больше о datetime2 (статья о MSDN на Transact-SQL)

источник изображения: MCTS Self-Paced Training Kit (экзамен 70-432): Microsoft® SQL Server® 2008 - Внедрение и обслуживание Глава 3. Таблицы -> Урок 1. Создание таблиц -> стр. 66

Иман
источник
7
Спасибо за показ, что статистика +1 для него, datetime2это здорово (Победитель)
Pankaj Parkar
2
@Iman Abidi: Согласно комментарию Оскара Берггрена от 10 сентября 2014 года в 15:51 к статье «SQLHINTS- DateTime Vs DateTime2», на которую вы ссылались: «datetime2 (3) НЕ совпадает с datetime. У них будет то же число цифр, но точность даты и времени равна 3,33 мс, а точность даты и времени2 (3) равна 1 мс. "
Том
1
@PankajParkar: Вау, не так быстро. Возможно, вы захотите взглянуть на раздел Минусы моего Ответа от 7/10/17 ниже.
Том
Как datetime2использовать меньше места для хранения, чем datetimeи в то же время предложить больший диапазон и более высокую точность?
Дай
107

Я согласен с @marc_s и @Adam_Poward - DateTime2 - предпочтительный метод продвижения вперед. Он имеет более широкий диапазон дат, более высокую точность и использует одинаковое или меньшее хранилище (в зависимости от точности).

Одно обсуждение пропустили, однако ...
@Marc_s заявляет: Both types map to System.DateTime in .NET - no difference there. Это верно, однако обратное неверно ... и это важно при выполнении поиска в диапазоне дат (например, "найди мне все записи, измененные 5/5/2010").

.NET версия Datetimeимеет схожий диапазон и точность DateTime2. При сопоставлении .net Datetimeсо старым SQL DateTimeпроисходит неявное округление . Старый SQL с DateTimeточностью до 3 миллисекунд. Это означает, что 11:59:59.997это как можно ближе к концу дня. Все, что выше, округляется до следующего дня.

Попробуй это :

declare @d1 datetime   = '5/5/2010 23:59:59.999'
declare @d2 datetime2  = '5/5/2010 23:59:59.999'
declare @d3 datetime   = '5/5/2010 23:59:59.997'
select @d1 as 'IAmMay6BecauseOfRounding', @d2 'May5', @d3 'StillMay5Because2msEarlier'

Избежание этого неявного округления является важной причиной для перехода на DateTime2. Неявное округление дат явно вызывает путаницу:

EBarr
источник
14
Вы также можете избежать этого округления, не пытаясь найти «конец» дня в любом случае. > = 5 мая и <6 мая намного безопаснее и будет работать с любым типом даты / времени (кроме ВРЕМЕНИ, конечно). Также предложите избегать региональных, неоднозначных форматов, таких как м / д / гггг.
Аарон Бертран,
2
@AaronBertrand - полностью согласен, но, глядя на количество вопросов, у нас есть вопрос, который, казалось, стоило описать.
EBarr
1
Почему вы перейти от 20100505к 5/5/2010? Прежний формат будет работать с любым регионом в SQL Server. Последний сломается: SET LANGUAGE French; SELECT Convert(datetime, '1/7/2015')oops:2015-07-01 00:00:00.000
ErikE
1
@EBarr: Re. «DateTime2 - предпочтительный метод продвижения вперед. Он имеет более широкий диапазон дат, более высокую точность и использует одинаковое или меньшее хранилище (в зависимости от точности»: я категорически не согласен. См. Раздел «Минусы» моего ответа от 7/10/17 ниже Короче говоря, эти преимущества, вероятно, не нужны (за пределами инженерных / научных приложений) и, следовательно, не стоят потери преимуществ, НАМНОГО более вероятно, гораздо более простой (даже с учетом обходных путей) способ неявного / явного преобразования в числовое значение с плавающей запятой ( # дней, включая, если это применимо, дроби с минимальной даты-времени) значение для +, - и в среднем
Том
20

Почти все ответы и комментарии были тяжелыми для плюсов и легкими минусами. Вот краткий обзор всех плюсов и минусов на данный момент, а также некоторые важные минусы (в # 2 ниже), которые я видел, упоминал только один раз или не упомянул вообще.

  1. ПЛЮСЫ:

1.1. Более совместимый с ISO (ISO 8601) (хотя я не знаю, как это проявляется на практике).

1.2. Большой диапазон (с 1/1/0001 по 12/31/9999 по сравнению с 1/1 / 1753-12 / 31/9999) (хотя дополнительный диапазон, все до 1753 года, скорее всего, не будет использоваться, кроме как, в исторических, астрономических, геологических и др. приложениях).

1.3. Точно совпадает с диапазоном диапазона .NET DateTimeтипа (хотя оба конвертируются туда и обратно без специального кодирования, если значения находятся в пределах диапазона и точности целевого типа, за исключением Con # 2.1 ниже, иначе произойдет ошибка / округление).

1.4. Более высокая точность (100 наносекунд, то есть 0,000,000,1 секунды против 3,33 миллисекунды, то есть 0,003,33 секунды) (хотя дополнительная точность, скорее всего, не будет использоваться, за исключением, например, в инженерных / научных приложениях).

1,5. При настройке на аналогичную (как в 1 миллисекунде не «ту же» (как в 3.33 миллисекундах), как утверждал Иман Абиди) точность, поскольку она DateTimeиспользует меньше места (7 против 8 байт), но тогда, конечно, вы потеряете прецизионная выгода, которая, вероятно, является одной из двух (другая - диапазон), которую чаще всего рекламируют, хотя, скорее всего, это и ненужные выгоды).

  1. МИНУСЫ:

2.1. При передаче параметра в .NET SqlCommandвы должны указать, можете System.Data.SqlDbType.DateTime2ли вы передавать значение за пределы DateTimeдиапазона и / или точности SQL Server , поскольку по умолчанию оно равноSystem.Data.SqlDbType.DateTime .

2.2. Невозможно неявно / легко преобразовать в числовое значение с плавающей запятой (число дней с минимальной даты и времени), чтобы выполнить следующие действия в / в выражениях SQL Server с использованием числовых значений и операторов:

2.2.1. добавить или вычесть # дней или неполных дней. Примечание. Использование DateAddфункции в качестве обходного пути не является тривиальным, когда нужно учитывать несколько, если не все части даты-времени.

2.2.2. возьмите разницу между двумя датами для расчета «возраста». Примечание. DateDiffВместо этого нельзя просто использовать функцию SQL Server , поскольку она не вычисляет, ageкак большинство людей ожидают в этом случае, если две даты-времени пересекают границу даты и времени календаря / часов, указанную в единицах измерения, даже если для крошечной дроби для этой единицы, она вернет разницу как 1 от этой единицы против 0. Например, DateDiffин Day-ы двух дат-раз с разницей всего в 1 миллисекунду вернут 1 против 0 (дней), если эти даты-времена в разные календарные дни (т.е. «1999-12-31 23: 59: 59.9999999» и «2000-01-01 00: 00: 00.0000000»). Те же самые значения даты-времени с разницей в 1 миллисекунду, если они не пересекают календарный день, возвращают значение «DateDiff» вDay '0 (дней).

2.2.3. возьмите Avgдату-время (в агрегированном запросе), просто преобразовав сначала «Float», а затем снова обратно в DateTime.

ПРИМЕЧАНИЕ. Для преобразования DateTime2в числовое значение необходимо выполнить что-то вроде следующей формулы, которая по-прежнему предполагает, что ваши значения не меньше, чем в 1970 году (что означает, что вы теряете весь дополнительный диапазон плюс еще 217 лет. Примечание. не сможет просто изменить формулу, чтобы учесть дополнительный диапазон, потому что вы можете столкнуться с проблемами переполнения чисел.

25567 + (DATEDIFF(SECOND, {d '1970-01-01'}, @Time) + DATEPART(nanosecond, @Time) / 1.0E + 9) / 86400.0- Источник: « https://siderite.dev/blog/how-to-translate-t-sql-datetime2-to.html »

Конечно, вы можете также Castв DateTimeпервый (и при необходимости снова к DateTime2), но вы потеряете точность и диапазон (все предшествующие года 1753) выгоды от DateTime2VS. DateTimeкоторые Prolly 2 самых больших и также одновременно Prolly 2 наименее вероятно, что необходимо, и напрашивается вопрос, зачем его использовать, когда вы теряете неявные / простые преобразования в числовые с плавающей запятой (# дней) для сложения / вычитания / "возраста" (против DateDiff) /Avg выгоды calcs, которая является большой по моему опыту.

Кстати, Avgдата-время является (или, по крайней мере, должно быть) важным вариантом использования. а) Помимо использования в получении средней продолжительности, когда дата-время (так как общая базовая дата-время) используются для представления длительности (обычная практика), б) также полезно получить статистику в виде панели мониторинга о том, какая средняя дата- время находится в столбце даты-времени диапазона / группы строк. c) Стандартный (или, по крайней мере, должен быть стандартным) специальный запрос для мониторинга / устранения неисправностей значений в столбце, которые могут быть недействительными никогда / больше и / или, возможно, должны быть признаны устаревшими, заключается в перечислении для каждого значения счетчика вхождений и (если таковые имеются) в Min, Avgи Maxштампы даты и времени , связанные с этим значением.

Том
источник
1
Как и противоположное представление - оно указывает на сторону уравнения c #. В сочетании со всеми другими «плюсами» это позволит людям сделать правильный выбор, исходя из того, где они хотят принять свою боль.
EBarr
1
@EBarr: Только часть «Против» моего «противоположного взгляда» «указывает на сторону уравнения c #». Остальные (Cons # 2.2.1 - 2.2.3), которые, как я сказал, являются гораздо более вероятными необходимыми преимуществами DateTime, связаны с влиянием на запросы и операторы SQL Server.
Том
По 2.2.1 - считается небезопасной практикой делать арифметику с датами, и предпочтительным способом всегда является использование DateAdd и связанных функций. Это лучшая практика. Существуют серьезные обязательства по выполнению арифметики дат, не в последнюю очередь это не работает для большинства типов дат. Несколько статей: sqlservercentral.com/blogs/… sqlblog.org/2011/09/20/…
RBerman
@RBerman: Re. «unsafe»: это небезопасно только для определенных типов дат (как DateTime2я уже упоминал (из-за высокой вероятности переполнения)). Число рейнольдса «не работает для большинства типов дат»: вам нужно, чтобы оно работало только с одним, и большинство дат в большинстве приложений, вероятно, никогда не потребуется преобразовывать в другой тип даты в течение всей их жизни (за исключением, может быть, как я уже упоминал , DateTime2чтобы DateTime(например, сделать «арифметику по датам»; P). Учитывая это, не стоит всего дополнительного кодирования не только в запрограммированных, но и в специальных исследовательских запросах использовать неарифметический дружественный тип даты.
Том
15

DateTime2 наносит ущерб, если вы - разработчик Access, пытающийся написать Now () для рассматриваемого поля. Просто выполнил миграцию Access -> SQL 2008 R2 и поместил все поля даты и времени в DateTime2. Добавление записи с Now () в качестве значения, полученного в результате взрыва. Это было хорошо 01.01.2012 14:53:04, но не 10.01.2012 14:53:04.

Однажды персонаж сделал разницу. Надеюсь, это кому-нибудь поможет.

Ретт Браун
источник
15

Вот пример, который покажет вам различия в размере хранилища (в байтах) и точности между smalldatetime, datetime, datetime2 (0) и datetime2 (7):

DECLARE @temp TABLE (
    sdt smalldatetime,
    dt datetime,
    dt20 datetime2(0),
    dt27 datetime2(7)
)

INSERT @temp
SELECT getdate(),getdate(),getdate(),getdate()

SELECT sdt,DATALENGTH(sdt) as sdt_bytes,
    dt,DATALENGTH(dt) as dt_bytes,
    dt20,DATALENGTH(dt20) as dt20_bytes,
    dt27, DATALENGTH(dt27) as dt27_bytes FROM @temp

который возвращается

sdt                  sdt_bytes  dt                       dt_bytes  dt20                 dt20_bytes  dt27                         dt27_bytes
2015-09-11 11:26:00  4          2015-09-11 11:25:42.417  8         2015-09-11 11:25:42  6           2015-09-11 11:25:42.4170000  8

Поэтому, если я хочу сохранить информацию до секунды, но не до миллисекунды, я могу сохранить 2 байта каждый, если я использую datetime2 (0) вместо datetime или datetime2 (7).

Baodad
источник
10

хотя с datetime2 повышена точность , некоторые клиенты не поддерживают date , time или datetime2 и вынуждают вас преобразовывать в строковый литерал. В частности, Microsoft упоминает проблемы ODBC, OLE DB, JDBC и SqlClient «нижнего уровня» с этими типами данных и имеет диаграмму, показывающую, как каждый может сопоставить тип.

Если значение совместимости выше точности, используйте datetime

Кулак ярости
источник
10

Старый вопрос ... Но я хочу добавить кое-что, что еще не было сказано кем-то здесь ... (Примечание: это мое собственное наблюдение, поэтому не спрашивайте никаких ссылок)

Datetime2 быстрее при использовании в критериях фильтра.

TLDR:

В SQL 2016 у меня была таблица с сотнями тысяч строк и столбцом datetime ENTRY_TIME, потому что требовалось хранить точное время до секунд. При выполнении сложного запроса со многими объединениями и подзапросом, когда я использовал выражение where как:

WHERE ENTRY_TIME >= '2017-01-01 00:00:00' AND ENTRY_TIME < '2018-01-01 00:00:00'

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

Execution Timeout Expired. The timeout period elapsed prior
to completion of the operation or the server is not responding.

Я удалил предложение where, и неожиданно запрос был выполнен за 1 секунду, хотя теперь были получены ВСЕ строки для всех дат. Я запускаю внутренний запрос с предложением where, и это заняло 85 секунд, а без предложения where - 0,01 секунды.

Я сталкивался здесь со многими потоками для этой проблемы как производительность фильтрации по времени и дате

Я немного оптимизировал запрос. Но реальная скорость, которую я получил, заключалась в изменении столбца datetime на datetime2.

Теперь тот же запрос, который был ранее заблокирован, занимает менее секунды.

ура

MJ Khan
источник
9

Интерпретация строк даты в datetimeи datetime2может отличаться, если используются DATEFORMATнастройки за пределами США . Например

set dateformat dmy
declare @d datetime, @d2 datetime2
select @d = '2013-06-05', @d2 = '2013-06-05'
select @d, @d2

Это возвращает 2013-05-06(т.е. 6 мая) для datetime, и 2013-06-05(т.е. 5 июня) для datetime2. Однако с dateformatset to mdy, оба @dи @d2возвращаются 2013-06-05.

Такое datetimeповедение противоречит документации MSDN, в SET DATEFORMATкоторой говорится: Некоторые форматы символьных строк, например ISO 8601, интерпретируются независимо от параметра DATEFORMAT . Очевидно, не правда!

Пока меня это не укусило, я всегда думал, что yyyy-mm-ddдаты будут обрабатываться правильно, независимо от настроек языка / локали.

Ричард Фосетт
источник
2
Нет. Для ISO 8601 я думаю, что вы имели в виду ГГГГММДД (без черточек). SET LANGUAGE FRENCH; DECLARE @d DATETIME = '20130605'; SELECT @d;Попробуйте еще раз с тире.
Аарон Бертран,
1
Стандарт допускает форматы ГГГГ-ММ-ДД и ГГГГММДД для представления календарных дат. Я думаю, что MSDN должен быть более конкретным в отношении того, какое подмножество спецификации ISO 8601 интерпретируется независимо!
Ричард Фосетт
1
Я знаю, что в SQL Server безопасен только синтаксис без тире.
Аарон Бертран
6

Согласно этой статье , если вы хотите иметь одинаковую точность DateTime с использованием DateTime2, вы просто должны использовать DateTime2 (3). Это должно дать вам ту же точность, занимать на один байт меньше и обеспечить расширенный диапазон.

jKlaus
источник
Для ясности, это та же точность, что и для SQL datetime, а не .NET DateTime.
Сэм Рюби
Это правильно, я предположил, что все будут понимать контекст, но это стоит особо указать.
jKlaus
4

Я просто наткнулся на еще одно преимущество DATETIME2: он позволяет избежать ошибки в adodbapiмодуле Python , которая взрывается, если datetimeпередается стандартное библиотечное значение, которое имеет ненулевые микросекунды для DATETIMEстолбца, но работает нормально, если столбец определен как DATETIME2.

Боб Клайн
источник
0
Select ValidUntil + 1
from Documents

Приведенный выше SQL не будет работать с полем DateTime2. Возвращается сообщение об ошибке «Тип операнда clash: datetime2 несовместим с int»

Добавление 1 для получения следующего дня - это то, что разработчики делали с датами в течение многих лет. Теперь у Microsoft есть супер новое поле datetime2, которое не может справиться с этой простой функциональностью.

«Давайте использовать этот новый тип, который хуже старого», я так не думаю!

Пол Маккарти
источник
2
Только таким образом мы ясно здесь datetimeи datetime2типы данных оба были введены в SQL Server 2008. Вы также получаете Operand type clash: date is incompatible with intот dateтипа , который был вокруг с первым днем точки. Все три типа данных работают очень хорошо, dateadd(dd, 1, ...)хотя.
AlwaysLearning
2
Это не ясно. У меня есть база данных SQLServer 2005 с полем datetime.
Пол МакКарти
0

Я думаю, что DATETIME2это лучший способ хранить date, потому что он имеет большую эффективность, чем DATETIME. В SQL Server 2008вы можете использовать DATETIME2, он хранит дату и время, занимает 6-8 bytesдля хранения и имеет точность 100 nanoseconds. Так что каждый, кто нуждается в большей точности времени, захочет DATETIME2.

Джеймс
источник