DateTime2 vs DateTime в SQL Server


762

Який:

це рекомендований спосіб дати і часу зберігання в SQL Server 2008+?

Мені відомо про відмінності в точності (і, ймовірно, місця для зберігання), але, не зважаючи на ті, на сьогоднішній день, чи є документ найкращої практики щодо того, коли використовувати що, або, можливо, нам слід просто користуватися datetime2лише?

Відповіді:


641

Документація MSDN для datetime рекомендує використовувати datetime2 . Ось їхня рекомендація:

Використовуйте time, date, datetime2і datetimeoffsetтип даних для нової роботи. Ці типи узгоджуються зі стандартом SQL. Вони більш портативні. time, datetime2і datetimeoffset забезпечити точність більше секунд. datetimeoffsetзабезпечує підтримку часового поясу для глобально розгорнутих додатків.

datetime2 має більший діапазон дат, більшу дробову точність за замовчуванням та необов'язкову точність, визначену користувачем. Також, залежно від визначеної користувачем точності, він може використовувати менше місця для зберігання.


46
в той час, як підвищена точність з datetime2, деякі клієнти не підтримують дату, час або datetime2 і змушують вас перетворитись на рядковий літерал. Якщо вас більше турбує порівнянність, ніж точність, використовуйте datetime
FistOfFury

4
Іншим варіантом є використання індексованого виду зі стовпцем, перетвореним як час дати для сумісності. Однак вам потрібно буде мати змогу вказувати додаток на вигляд.
TamusJRoyce

9
Підтримка часового поясу за допомогою DATETIMEOFFSET - неправильне значення. Він зберігає лише зміщення UTC за певний момент часу, а не часовий пояс.
Suncat2000

6
@Porad: Яка саме користь на практиці бути "більш портативною" завдяки тому, що вона є "SQL Standard"? Це, крім того, що ви пишете значно більше коду, який значно менш читабельний / підтримується для "порту" в інший RDBMS, який Мабуть, ніколи не трапляється протягом життя цього коду. Окрім можливо інструментів та драйверів SQL Server, передбачених Microsoft (якщо вони навіть є), чи є додатки, які фактично покладаються на конкретні представлення на рівні Bit DateTime2(типу) або будь-який інший сервер SQL Введіть цю проблему)? Дивіться мінуси в моїй відповіді 7.10.17 нижче, чому я прошу.
Том

2
@ Адам Порад: Крім того, всі ці переваги, ймовірно, непотрібні (за межами інженерних чи наукових програм), і тому не варто втрачати вигоди набагато, МНОГО швидше потрібні: набагато простіша (навіть з огляду на обхідні шляхи) можливість неявного / явного перетворення на числове число з плаваючою комою (число днів, у тому числі, якщо додатк., дробові дні з моменту дати-часу) для додавання, віднімання, мінімумів, максимумів та середніх значень. Дивіться мінуси в моєму відповіді 7.10.17 нижче для отримання детальної інформації.
Том

493

DATETIME2має діапазон дат від " DATETIME0001/01/01" до "9999/12/31 ", тоді як тип підтримує лише рік 1753-9999.

Крім того, якщо вам потрібно, ви DATETIME2можете бути точнішими за часом; DATETIME обмежений 3 1/3 мілісекунди, при цьому DATETIME2може бути точним до 100ns.

Обидва типи відображаються System.DateTimeв .NET - різниці немає.

Якщо у вас є вибір, я б рекомендував використовувати, DATETIME2коли це можливо. Я не бачу ніяких переваг у використанні DATETIME(за винятком зворотної сумісності) - у вас виникнуть менше проблем (з датами, що виходять за межі діапазону, і таких клопотів).

Плюс: якщо вам потрібна лише дата (без часової частини), використовуйте ДАТУ - це так само добре, як DATETIME2і економить місце також! :-) Те ж саме стосується часу - використання TIME. Ось для чого ці типи існують!


158
Будьте обережні, додаючи значення .NET DateTime як параметр до SqlCommand, оскільки він любить припускати, що це старий тип дати, і ви отримаєте помилку, якщо спробуєте написати значення DateTime, яке перевищує діапазон 1753-9999 років якщо ви чітко не вказали тип як System.Data.SqlDbType.DateTime2 для SqlParameter. У будь-якому випадку, datetime2 чудовий, тому що він може зберігати будь-яке значення, яке може бути збережене у типі .NET DateTime.
Трайнко

10
@marc_s - Чи не для цього нуль?
JohnFx

8
@JohnFX - тут трохи запізнюється, - але ви не встановите нульовий час дати. ви б використовували Nullable <datetime> або datetime? який обробляє null просто чудово - і при зіставлянні з програмою просто зробить param.value = someDateTime ?? DBValue.Null Його нещасна ми застрягли з типом даних з номером після - тільки здається , так що «загальний» :)
Adam Tuliper - MSFT

69
Лол, я просто намагався підтримати власний коментар (вище), перш ніж зрозумів, що це мій власний коментар (зроблений понад рік тому). Я все ще маю справу з німим дизайнерським рішенням .NET Framework, щоб TRUNCATE всі значення DateTime за замовчуванням, коли вони передаються як SqlParameters, якщо ви явно не встановите його на більш точний SqlDbType.DateTime2. Стільки для автоматичного виведення правильного типу. Дійсно, вони повинні були зробити зміни прозорими, замінивши менш точну, менш ефективну реалізацію з обмеженим діапазоном, і зберегли початкове ім'я типу "datetime". Дивіться також 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 Vs DateTime2 )
  • Дізнайтеся більше про datetime2 (стаття Transact-SQL MSDN)

Джерело зображення: Навчальний комплект MCTS з автоматичним темпом (Іспит 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) НЕ є такою ж, як дата. Вони матимуть однакову кількість цифр, але точність дати - 3,33 мс, тоді як точність дати2 (3) - 1 мс ".
Том

1
@PankajParkar: Вау, не так швидко. Ви можете переглянути розділ Мінуси мого відповіді від 10.10.17 нижче.
Том

Як datetime2використовувати менше місця для зберігання, datetimeале при цьому пропонувати більший асортимент та більш високу точність?
Дай

107

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

Одне обговорення пропустили, проте ...
@Marc_s заявляє: Both types map to System.DateTime in .NET - no difference there. Це правильно, однак, зворотне не відповідає дійсності ... і це має значення під час пошуку діапазону дат (наприклад, "знайти мені всі записи, змінені 5.05.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. Неявне закріплення дат явно викликає плутанину:


14
Ви також можете уникнути цього округлення, не намагаючись знайти «кінець» дня. > = 5 травня І <6 травня набагато безпечніше і працюватиме над будь-яким із типів дати / часу (крім TIME, звичайно). Також пропонуємо уникати регіональних, неоднозначних форматів, таких як m / d / yyyy.
Аарон Бертран

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 - кращий метод, який рухається вперед. Він має більш широкий діапазон дат, більш високу точність і використовує рівну або меншу сховище (залежно від точності": я категорично не погоджуюся. Дивіться розділ "Мінуси" мого відповіді від 10.10.17 нижче Коротше кажучи, ці переваги, ймовірно, непотрібні (поза інженерними / науковими програмами), і тому не варто втрачати вигоди, ЩО БОЛЬШ імовірніші, ніж набагато простіші (навіть з огляду на обхідні шляхи) можливості неявного / явного перетворення в числовий з плаваючою комою ( # днів, у тому числі, якщо додатк., дроби від мінімальної дати-часу) значення для +, - та сер.
Том

20

Майже всі відповіді та зауваження були важкими щодо плюсів і світла проти мінусів. Ось резюме всіх плюсів і мінусів поки що, а також деякі найважливіші мінуси (у №2 нижче), які я бачив лише один раз, або взагалі немає.

  1. ПРО:

1.1. Більше ISO-сумісний (ISO 8601) (хоча я не знаю, як це на практиці втілюється).

1.2. Більш широкий діапазон (1/1/0001 до 31.12.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 , оскільки воно за замовчуванням 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" через Day0 (днів).

2.2.3. візьміть Avgдату-раз (у сукупному запиті), просто перейшовши спочатку на "Плаваючий", а потім знову на 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) / Avgcalcs, що є великим в моєму досвіді.

До речі, час Avgдат є (або принаймні повинен бути) важливим випадком використання. а) Окрім використання для отримання середньої тривалості, коли для подання тривалості (загальної практики) використовуються дати-дати (оскільки загальна основна дата-час); б) також корисно отримати статистику типу інформаційної панелі щодо того, яка середня дата- час - у стовпці дата-час діапазону / групи рядків. в) Стандартний (або принаймні повинен бути стандартним) спеціальний запит для моніторингу / усунення несправностей у стовпці, який може бути недійсним ніколи / більше, та / або може бути необхідним для застарілого, - це перелік кожного значення кількості випадків і (якщо такі є) в Min, Avgі Maxштампи дати і часу , пов'язані з цим значенням.


1
Як і протилежний погляд - він вказує на c # сторону рівняння. У поєднанні з усіма іншими "плюсами" це дозволить людям зробити вдалий вибір, виходячи з того, де вони хочуть взяти свій біль.
EBarr

1
@EBarr: Тільки мінуси №1 частини мого "" протилежного погляду "" "вказують на c # сторону рівняння". Решта (мінуси 2.2.1 - 2.2.3), які, як я вже сказав, є набагато більш імовірними необхідними перевагами ( DateTime), усі пов'язані з впливом на запити та заяви SQL Server.
Том

Re 2.2.1 - вважається небезпечною практикою робити арифметику на датах, і кращим способом завжди є використання DateAdd та пов'язаних з ними функцій. Це найкраща практика. Існують серйозні зобов’язання щодо виконання арифметики дат, не останнє з яких - це не працює для більшості типів дат. Кілька статей: sqlservercentral.com/blogs/… sqlblog.org/2011/09/20/…
RBerman

@RBerman: Re. "небезпечно": це небезпечно лише для певних типів дати (наприклад, DateTime2я вже згадував (через велику ймовірність переповнення)). Re. "не працює для більшості типів дат": вам потрібен лише для роботи з одним, і більшість дат у більшості додатків, ймовірно, ніколи не потребуватимуть перетворення на інший тип дати протягом усього їхнього життя (за винятком, можливо, як я вже згадував , DateTime2щоб DateTime(наприклад, робити "арифметику на датах"; P). Враховуючи, що не варто вводити додаткове кодування не тільки запрограмованих, але й спеціальних пошукових запитів, щоб використовувати неарифметичний дружній тип дати.
Том

15

DateTime2 викликає погіршення, якщо ви розробник доступу, який намагається написати Now () у відповідне поле. Тільки що зробив міграцію Access -> SQL 2008 R2, і всі поля дати ставив у вигляді DateTime2. Додавання запису з Now (), коли значення вибуховане. Це було добре 1.01.2012 14:53:04, але не 1.10.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).


10

в той час, як підвищена точність з datetime2 , деякі клієнти не підтримують дату , час або datetime2 і змушують вас перетворитись на рядковий літерал. Зокрема, Microsoft згадує проблеми "ODBC", OLE DB, JDBC та SqlClient "нижчого рівня" з цими типами даних і має діаграму, яка показує, як кожен може зіставити тип.

Якщо можна порівняти значення над точністю, використовуйте timetime


10

Старе запитання ... Але я хочу додати щось, про що тут ніхто не говорив ... (Примітка. Це моє власне спостереження, тому не вимагайте посилань)

Datetime2 швидше, коли використовується в критеріях фільтра.

TLDR:

У SQL 2016 у мене була таблиця із сотнями тисяч рядків і стовпець дати ENTRY_TIME, оскільки потрібно було зберігати точний час до секунд. Під час виконання складного запиту з безліччю приєднань і підзапиту, коли я використовував пункт, як:

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.

Я видалив пункт де, і несподівано запит був запущений за 1 сек, хоча тепер усі ВІДНОВІ рядки для всіх дат отримано. Я запускаю внутрішній запит із пунктом де, і це зайняло 85 секунд, а без пункту, де це зайняло 0,01 сек.

Я зіткнувся з багатьма темами для цієї проблеми, як ефективність фільтрування за датою

Я трохи оптимізував запит. Але реальна швидкість, яку я отримала, змінила стовпець datetime на datetime2.

Тепер той самий запит, який минув раніше, займає менше секунди.

ура


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 травня) за datetime2013-06-055 червня) для datetime2. Однак з dateformatвстановленим на mdy, і те, @dі @d2повернення 2013-06-05.

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

Поки мене не покусало, я завжди думав, що yyyy-mm-ddдати просто обробляються правильно, незалежно від налаштувань мови / мови.


2
Ні. Для ISO 8601 я думаю, що ви мали на увазі YYYYMMDD (без тире). SET LANGUAGE FRENCH; DECLARE @d DATETIME = '20130605'; SELECT @d;Спробуйте ще раз з тире.
Аарон Бертран

1
Стандарт передбачає формати YYYY-MM-DD та YYYYMMDD для представлення календарних дат. Я думаю, що MSDN має бути більш конкретним щодо того, який підмножина специфікації ISO 8601 інтерпретується самостійно!
Річард Фосетт

1
Я це знаю, але в SQL Server безпечний лише синтаксис без тире.
Аарон Бертран

6

Згідно з цією статтею , якщо ви хочете мати однакову точність DateTime за допомогою DateTime2, ви просто повинні використовувати DateTime2 (3). Це повинно дати вам таку ж точність, займати на один менший байт і забезпечити розширений діапазон.


Щоб було зрозуміло, це та ж точність, що і SQL datetime, а не .NET DateTime.
Сем Рюбі

Це правильно, я припускав, що кожен зрозуміє контекст, але його варто конкретно констатувати.
jKlaus

4

Я просто натрапив на ще одну перевагу DATETIME2: він дозволяє уникнути помилки в adodbapiмодулі Python , який вибухає, якщо datetimeпередається стандартне значення бібліотеки , яке має ненульові мікросекунди для DATETIMEстовпця, але працює добре, якщо стовпець визначений як DATETIME2.


0
Select ValidUntil + 1
from Documents

Наведений вище SQL не працюватиме з полем DateTime2. Він повертається та помилка "Зіткнення типу Operand: datetime2 несумісний з int"

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

"Давайте використовувати цей новий тип, який гірший за старий", я не думаю, що так!


2
Тільки таким чином ми ясно тут datetimeі datetime2типи даних обидва були введені в SQL Server 2008. Ви також отримуєте Operand type clash: date is incompatible with intвід dateтипу , який був навколо з першим днем точки. Всі три типи даних добре працюють, dateadd(dd, 1, ...)хоча.
Завжди навчання

2
Це не ясно. У мене база даних SQLServer 2005 з полем дати в ній.
Пол Маккарті

0

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

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