Предпочтительный способ хранения DateTime

18

Мы можем хранить информацию о дате и времени несколькими способами. Каков наилучший подход для хранения информации DateTime?

Сохранение даты и времени в 2 отдельных столбцах или в одном столбце с использованием DateTime ?

Можете ли вы объяснить, почему этот подход лучше?

(Ссылка на документы MySQL для справки, вопрос общий, не специфичный для MySQL)
Типы даты и времени : дата и время

юлианский
источник
3
Это во многом зависит от того, какую систему баз данных вы используете. Для чего стоит: Oracle решил сделать это как один столбец (как тип данных DATETIME), и в этом случае использование их встроенной поддержки, безусловно, будет лучше, чем сохранение этой информации в 2 столбцах в виде типов данных NUMBER (даже если вы только нужна 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.

Вот пара примеров:

  • Вы регистрируете поездки на автомобиле для налоговых вычетов. Зная точное время поездки, было бы полезно, но если сотрудник не записал это и забыл, дата все равно должна быть записана сама (требуемая дата, дополнительное время).

  • Вы проводите опрос, чтобы выяснить, во сколько люди едят свой обед, и вы просите участников заполнить форму с указанием времени их обеда, включая даты. Некоторые не заботятся о заполнении даты, и вы не хотите сбрасывать данные, поскольку это время, которое вас действительно волнует (необязательная дата, требуемое время).

Смотрите этот связанный вопрос для альтернативных подходов.

JBentley
источник
В RFC 3339 существует соглашение для записи «неизвестного локального смещения». Я не думаю, что это вполне покрывает случай использования «неизвестного времени», но это близко. Следующий раздел «неквалифицированное местное время» еще ближе, но, опять же, этого недостаточно.
geneorama
Да, из-за этого я сейчас смотрю на ствол рефакторинга своей схемы. Возьмите ситуацию с прокатом автомобилей. Чтобы забрать автомобиль у прокатной компании - компания должна быть открыта; поэтому вы указываете дату и время для получения. Тем не менее, у многих есть коробочки для ключей; так что вы отвалите после нескольких часов. Так что, если место закрыто по воскресеньям; есть дата сдачи; но не время Сохранение значения 0 (например, 12:00) не будет работать, потому что некоторые места открыты до полуночи, что является допустимым значением в других ситуациях.
Рис
5

Я всегда предпочитаю хранить это как один столбец, если нет особого спроса на бизнес / приложения. Ниже приведены мои очки -

  • Извлечение времени из отметки времени не проблема
  • Зачем добавлять дополнительный столбец только на время, если мы можем хранить оба вместе
  • Чтобы избежать добавления даты и времени каждый раз, когда вы запрашиваете.
Ашвини Мохан
источник
1
@a_horse_with_no_name имеет смысл здесь. Я думаю, что «Извлечение метки времени из даты и времени не является проблемой» следует перефразировать как «Извлечение времени из метки времени не является проблемой» . «Метка времени» обычно означает дату и время (и обычно часовой пояс).
ypercubeᵀᴹ
Да, согласен @ ypercubeᵀᴹ. Отметка времени обычно означает дату и время. Я явно упомянул слово DateTimeStamp, чтобы каждый мог понять, что речь идет о дате и времени. Но вы тоже правы. Модифицировал ответ.
Ашвини Мохан
3

В SQL Server лучше всего хранить DataTime как одно поле. Если вы создаете индекс для столбца DataTime, его можно использовать как поиск по дате и как поиск по времени. Поэтому, если вам нужно ограничить все записи, существующие на определенную дату, вы все равно можете использовать индекс, не делая ничего особенного. Если вам нужно запросить часть времени, вы не сможете использовать тот же индекс, и, следовательно, если у вас есть экономическое обоснование, в котором вы заботитесь о времени дня больше, чем DateTime, вы должны хранить его отдельно, так как вам потребуется создать индекс на это и улучшить производительность.

Владимир Осельский
источник
1

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

Иван
источник
2
Да, есть: это timestampто, что определяет стандарт SQL. Хранение меток времени в виде строк - очень плохой совет
a_horse_with_no_name
0

Прочитав кучу вещей, UTC Unix время в BIGINT кажется оптимальным решением. TZDB ID временного идентификатора в VARCHAR для хранения часового пояса, если это необходимо. Несколько аргументов:

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

  2. BIGINT (8 КБ) по крайней мере настолько же легок или легче, чем DECIMAL, требуемый для хранения в формате xxxxxx.xxxxxx, которое MySQL практически хранит в виде двух INT + что-то еще . И этого достаточно, чтобы хранить века вперед.

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

  4. Математические операции с BIGINT должны быть такими же быстрыми и быстрыми, как и все остальное на любом оборудовании.

Конечно, все вышесказанное актуально для крупных международных проектов. Для чего-то небольшого, достаточно выбрать формат по умолчанию для выбранной платформы.

Артур Тарасов
источник
2
« сделать кучу бесполезных преобразований в фоновом режиме, которые кажутся ... неясными » - о какой СУБД вы говорите? Для timestampстолбца не происходит «бесполезных преобразований» (на уровне базы данных), и timestamp with time zoneэто хорошо задокументировано и объяснено в руководствах (по крайней мере, для Oracle и Postgres)
a_horse_with_no_name
1
«Практически все основные языки программирования имеют библиотеки стандартных функций для работы со временем Unix». И все же вы выбрасываете все библиотеки и функции о датах, датах и ​​временах, которые есть у SQL / СУБД, с вашим выбором использования bigint ...
ypercubeᵀᴹ