Бажаний спосіб зберігання DateTime


18

Інформацію про дату та час ми можемо зберігати двома способами. Який найкращий підхід для зберігання інформації DateTime?

Збереження дати та часу у 2 окремих стовпцях або одному стовпчику за допомогою DateTime ?

Чи можете ви пояснити, чому такий підхід кращий?

(Посилання на документи MySQL для довідок, питання загальне, не характерне для MySQL)
Типи дати та часу : дата та час


3
Це багато в чому залежить від того, яку систему бази даних ви використовуєте. Для чого це варто: Oracle вирішив зробити це як один стовпець (як тип даних DATETIME), і в цьому випадку використання вбудованої підтримки, безумовно, буде кращим, ніж зберігання цієї інформації у 2 стовпцях як НОМЕРИ типів даних (навіть якщо ви тільки потрібна 1 частина для даного запиту ... дата або час).
Кріс Джонстон

5
Для SQL Server один випадок, коли можна віддати перевагу, - це групування за датою. Поточний агрегат можна буде використовувати без сортування для складеного індексу на date,time з, group by dateале не для індексу на datetime з, group by cast(datetime as date)хоча він забезпечить бажане замовлення.
Мартін Сміт

1
Зауважте, що будь-яка математика за значеннями часу вимагає знати дату та часовий пояс - наприклад, відстань між двома разів залежить від того, чи є цей день подія DST, деякі дні мають 23 або 25 годин, а також існують високосні секунди.
Петерсіс

Відповіді:


23

Збереження даних в одному стовпчику є кращим способом, оскільки вони нерозривно пов'язані. Момент часу - це цілий інформаційний матеріал, а не два.

Поширений спосіб зберігання даних про дату / час, використовуваний "поза кадром" багатьма продуктами, - це перетворення їх у десяткове значення, де "дата" є цілою частиною десяткового значення, а "час" - дробовою. значення. Так, 1900-01-01 00:00:00 зберігається як 0,0, а 20 вересня 2016 9:34:00 зберігається як 42631,39861. 42631 - кількість днів з 1900-01-01. .39861 - це час, що минув з півночі. Не використовуйте для цього безпосередньо десятковий тип, використовуйте явний тип дати / часу; моя думка тут - лише ілюстрація.

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

Якщо ви зберігаєте значення окремо, ви незмінно наштовхуєтесь на "помилки", які важко виявити. Візьмемо для прикладу наступне:

IF OBJECT_ID('tempdb..#DT') IS NOT NULL
DROP TABLE #DT;
CREATE TABLE #DT
(
    dt_value DATETIME NOT NULL
    , d_value DATE NOT NULL
    , t_value TIME(0) NOT NULL
);


DECLARE @d DATETIME = '2016-09-20 09:34:00';

INSERT INTO #DT (dt_value, d_value, t_value)
SELECT @d, CONVERT(DATE, @d), CONVERT(TIME(0), @d);

SET @d = '2016-09-20 11:34:00';

INSERT INTO #DT (dt_value, d_value, t_value)
SELECT @d, CONVERT(DATE, @d), CONVERT(TIME(0), @d);

/* show all rows with a date after 2016-07-01 11:00 am */
SELECT *
FROM #DT dt
WHERE dt.dt_value >= '2016-07-01 11:00:00';

/* show all rows with a date after 2016-07-01 11:00 am */
SELECT *
FROM #DT dt
WHERE dt.d_value >= CONVERT(DATE, '2016-07-01')
    AND dt.t_value >= CONVERT(TIME(0), '11:00:00');

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

введіть тут опис зображення

Правильний спосіб фільтрації діапазону дати / часу, коли значення знаходяться в дискретних стовпцях, на що в коментарях вказував @ypercube:

WHERE dt.d_value > CONVERT(DATE, '2016-07-01') /* note there is no time component here */
    OR (
        dt.d_value = CONVERT(DATE, '2016-07-01') 
        AND dt.t_value >= CONVERT(TIME(0), '11:00:00')
    )

Якщо вам потрібен розділений часовий компонент для цілей аналізу , ви можете розглянути можливість додавання обчисленої, збереженої стовпця для часової частини значення:

ALTER TABLE #DT
ADD dt_value_time AS CONVERT(TIME(0), dt_value) PERSISTED;

SELECT *
FROM #dt;

введіть тут опис зображення

Стійкий стовпчик може бути індексований, що дозволяє робити швидкі сортування тощо за часом дня.

Якщо ви плануєте розділити дату і час на два поля для цілей відображення, ви повинні усвідомити, що форматування повинно здійснюватися у клієнта, а не на сервері.


11

На інші відповіді я буду висловлювати незгодну думку.

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

Тим НЕ менше, це може бути так , що один або обидва компонента по окремо необов'язково. У такому випадку було б неправильно зберігати його в одній колонці. Це може змусити вас представляти значення NULL довільним способом, наприклад, зберігаючи час як 00:00:00.

Ось кілька прикладів:

  • Ви реєструєте поїздки на транспортних засобах для сплати податку на пробіг. Знати точний час подорожі було б корисно, але якщо працівник цього не записав і забув, дата все одно повинна бути записана сама (потрібна дата, необов'язковий час).

  • Ви проводите опитування, щоб дізнатися, в який час люди їдять свій обід, і ви просите учасників заповнити анкету із зразком їхнього часу обіду, включаючи дати. Деякі не заважають заповнювати дату, і ви не хочете відкидати дані, оскільки це саме час, який вам дійсно важливий (необов'язкова дата, необхідний час).

Дивіться це пов'язане питання щодо альтернативних підходів.


У RFC 3339 існує конвенція щодо запису "невідомого локального зміщення". Я не думаю, що він цілком охоплює випадок використання "невідомого часу", але це близько. Наступний розділ «Некваліфікований місцевий час» ще ближче, але знову ж таки це недостатньо.
генеорама

Так, я зараз дивлюся на бочку рефакторингу своєї схеми через це. Прийміть ситуацію з прокатом автомобіля. Щоб забрати автомобіль у орендної компанії - компанія повинна бути відкритою; тому ви визначаєте дату та час для пікапа. Однак у багатьох є коробки для ключів; тож ви відпадаєте через години. Тож якщо місце розташування закрито по неділях; є дата вибування; але не час. Збереження значення 0 (наприклад, 12:00) не буде працювати, оскільки деякі місця відкриті до півночі, що є дійсним значенням в інших ситуаціях.
Рис

5

Я завжди вважаю за краще зберігати це як один стовпець, якщо не існує певного попиту на бізнес / заявку. Нижче мої моменти -

  • Витягнути час із мітки часу - це не проблема
  • Навіщо додавати додатковий стовпець лише на час, якщо ми можемо зберігати обидва разом
  • Щоб уникнути додавання дати та часу кожен раз, коли ви запитуєте.

1
У @a_horse_with_no_name тут є пункт. Я думаю, що "Вилучення часової позначки з дати мітки не є проблемою" слід перефразовувати як "Вилучення часу з мітки часу не є проблемою" . "Часова позначка" зазвичай означає дату і час (і, як правило, часовий пояс).
ypercubeᵀᴹ

Так, згоден @ ypercubeᵀᴹ. Часова позначка зазвичай означає дату і час. Я чітко згадав слово DateTimeStamp, тому кожен може зрозуміти, що ми говоримо про дату і час обох. Але ви також правильні. Відповідь змінили.
Ашвіні Мохан

3

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


1

Дійсно, шкода, що для цього немає стандартного типу крос-СУБД (наприклад, INT і VARCHAR призначені для цілих чисел та рядкових значень). У двох підходах до баз даних, з якими я вже зустрічався, використовуються стовпці VARCHAR / CHAR для зберігання значень DataTime у вигляді рядків, відформатованих відповідно до стандарту ISO 8601 (зручніший для людей, що читаються) та використовуючи BIGINT для зберігання їх як часових позначок POSIX (зберігається більше ефективно, швидше, простіше маніпулювати математично).


2
Так, є: timestampсаме це визначає стандарт SQL. Зберігання міток часу як рядків - дуже погана порада
a_horse_with_no_name

0

Прочитавши купу матеріалів, час Unix у BIGINT представляється оптимальним рішенням. Ідентифікатор часового поясу TZDB у VARCHAR для зберігання часового поясу, якщо це необхідно. Кілька аргументів:

  1. TIMESTAMP і DATETIME роблять купу примхливих перетворень на задньому плані, які здаються складними і не зрозумілими. Сервер перемикається з місцевого часу на UTC або на час та назад сервера, іноді чи ні. Купа прихованих накладних для кожної функції.

  2. BIGINT (8kb) принаймні такий же легкий або легший, ніж DECIMAL, необхідний для зберігання формату xxxxxx.xxxxxx , який практично зберігається як два INTs + щось у MySQL . І цього достатньо для зберігання на віки вперед.

  3. Практично всі основні мови програмування мають бібліотеки стандартних функцій для роботи з часом Unix.

  4. Операції з математики з BIGINT повинні бути швидкими або швидшими, ніж будь-що інше на будь-якому апаратному забезпеченні.

Звичайно, все вищесказане стосується великих міжнародних проектів. Для чогось невеликого, достатньо вдалого виглядає форматування обраного фрейму за замовчуванням.


2
" зробити купу химерних перетворень на задньому плані, які здаються ... не зрозумілими " - про які СУБД ви говорите? Для timestampстовпця не відбувається "химерних перетворень" (на рівні бази даних), і timestamp with time zoneце добре задокументовано та пояснено в посібниках (принаймні для Oracle та Postgres)
a_horse_with_no_name

1
"Насправді всі основні мови програмування мають бібліотеки стандартних функцій для роботи з часом Unix." І все-таки ви викидаєте всі бібліотеки та функції щодо дат, дат і часових позначок, які мають SQL / СУБД, з вибором використання bigint ...
ypercubeᵀᴹ
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.