Лучший способ хранить время (чч: мм) в базе данных


101

Я хочу хранить время в таблице базы данных, но мне нужно хранить только часы и минуты. Я знаю, что могу просто использовать DATETIME и игнорировать другие компоненты даты, но как лучше всего сделать это, не сохраняя больше информации, чем мне действительно нужно?


Что эквивалентно ВРЕМЕНИ для sql server 2005?
Эрран Морад,

@BoratSagdiyev в SQL Server 2005 его нет, поэтому я и задал вопрос.
Мэтью Дрессер,

Ответы:


135

Вы можете сохранить его как целое число минут после полуночи:

например.

0 = 00:00 
60 = 01:00
252 = 04:12

Однако вам нужно будет написать код, чтобы восстановить время, но это не должно быть сложно.


8
Затем используйте DATEADD (), чтобы вернуть реальное время. Даже smallint будет достаточно.
Джоэл Кохорн,

36
был там, сделал это ... mins = dd% 60 и hours = dd / 60 на ints делает свое дело.
Усама Аль-Маадид,

2
Это отличная, простая и понятная идея. +1
Whkeysierra

7
небольшой недостаток - отсутствие читабельности в базе данных
Jowen

нам нужно сохранить какой-то вариант дней, часов и минут; тип данных Time в SQL Server работает только до 23:59:59, поэтому мы не могли его использовать. Вдохновленные вашим ответом, мы решили создать три столбца int для дней: часов: минут для максимальной гибкости. Спасибо!
matao


15

DATETIME начало DATETIME конец

Я умоляю вас использовать вместо этого два значения DATETIME , обозначенные чем-то вроде event_start и event_end .

Время - сложный бизнес

В настоящее время большая часть мира правильно или ошибочно приняла метрическую систему на основе Денери для большинства измерений. В целом это хорошо, потому что, по крайней мере, мы все можем согласиться с тем, что ag, это мл, это кубический см. По крайней мере, примерно так. Метрическая система имеет много недостатков, но, по крайней мере, она неизменно ошибочна на международном уровне.

Однако со временем у нас есть; 1000 миллисекунд в секунду, от 60 секунд до минуты, от 60 минут до часа, 12 часов на каждые полдня, примерно 30 дней в месяц, которые варьируются в зависимости от месяца и даже года, в каждой стране есть свое временное смещение относительно других , формат времени зависит от страны.

Это сложно переварить, но в целом для такого сложного сценария невозможно найти простое решение.

Некоторые углы можно обрезать, но есть такие, где разумнее не

Хотя главный ответ здесь предполагает, что вы храните целое число минут после полуночи, это может показаться вполне разумным, я научился избегать этого на горьком опыте.

Причины для реализации двух значений DATETIME заключаются в повышении точности, разрешения и обратной связи.

Все это очень удобно, когда дизайн дает нежелательные результаты.

Я храню больше данных, чем требуется?

Сначала может показаться, что хранится больше информации, чем мне нужно, но есть веская причина принять этот удар.

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

Это огромная планета

В прошлом я был виноват в том, что игнорировал то, что на этой планете есть и другие страны, помимо моей. В то время это казалось хорошей идеей, но это ВСЕГДА приводило к проблемам, головным болям и потере времени впустую. ВСЕГДА учитывайте все часовые пояса.

C #

DateTime хорошо отображается в строке в C #. Метод ToString (формат строки) компактен и удобен для чтения.

Например

new TimeSpan(EventStart.Ticks - EventEnd.Ticks).ToString("h'h 'm'm 's's'")

SQL сервер

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

Например

SELECT DATEDIFF(MINUTE, event_start, event_end)

Стандарт даты ISO8601

Если вы используете SQLite, этого у вас нет, поэтому вместо этого используйте текстовое поле и сохраните его в формате ISO8601, например.

«2013-01-27T12: 30: 00 + 0000»

Ноты:

  • Используется 24-часовой формат *

  • Часть смещения времени (или +0000) в стандарте ISO8601 напрямую сопоставляется со значением долготы координаты GPS (без учета перехода на летнее время или по стране).

Например

TimeOffset=(±Longitude.24)/360 

... где ± относится к восточному или западному направлению.

Поэтому стоит подумать, стоит ли хранить долготу, широту и высоту вместе с данными. Это зависит от приложения.

  • ISO8601 - это международный формат.

  • В вики можно найти более подробную информацию по адресу http://en.wikipedia.org/wiki/ISO_8601 .

  • Дата и время сохраняются в международном формате, а смещение записывается в зависимости от того, где в мире было сохранено время.

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

Дополнительная консультация бесплатно

Также стоит сгруппировать события в цепочку. Например, при записи гонки все событие может быть сгруппировано по racer, race_circuit, circuit_checkpoints и circuit_laps.

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

Чем больше вы вкладываете, тем больше вы получаете

Я полностью понимаю желание экономить пространство, насколько это возможно, но я редко делал это за счет потери информации.

Эмпирическое правило с базами данных гласит, что, как сказано в названии, база данных может сказать вам столько, сколько у нее есть данные, и может быть очень дорого возвращаться к историческим данным, заполняя пробелы.

Решение состоит в том, чтобы исправить это с первого раза. Это, конечно, легче сказать, чем сделать, но теперь у вас должно быть более глубокое представление об эффективном проектировании базы данных, и впоследствии у вас будет гораздо больше шансов сделать это правильно с первого раза.

Чем лучше будет ваш первоначальный проект, тем дешевле будет впоследствии ремонт.

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


2
Ваш комментарий «Часть +0000 в ISO8601 отображает прямую широту в координатах GPS» неверен. Он представляет собой смещение от всемирного
Гэри Уокер,

10

Просто сохраните обычную дату и время и игнорируйте все остальное. Зачем тратить лишнее время на написание кода, который загружает int, манипулирует им и преобразует в datetime, если вы можете просто загрузить datetime?


3
Одна из возможных причин может заключаться в экономии места на жестком диске - DATETIMEтип данных занимает 4 байта для хранения, в то время как, SMALLINTнапример, только четверть байта . Нет большой разницы, если у вас всего несколько тысяч строк, но если у вас много миллионов строк, как у многих компаний, тогда ваша экономия места будет значительной.
Шеридан

32
Возможно, но учтите, что на этот вопрос ответили 12 человек, каждому из которых, скажем, потребовалось 15 минут, чтобы подумать. Предположим, что все они программисты и зарабатывают 50 долларов в час. Ценой времени, потраченного на размышления об этой проблеме, можно было бы купить новый блестящий жесткий диск емкостью 2 ТБ для хранения дополнительных байтов.
Сет,

@ Правильно, если мы говорим только о лишних байтах. Но ваше предложение может быть только в том случае, если это небольшой проект с 1-2 разработчиками. Но это будет большой проект с множеством разработчиков (плюс QA), и когда новый программист попытается в этом разобраться, это будет большой проблемой. Увы, все мы зависим от бизнес-логики.
Посредник

При использовании облачных сервисов экономия дискового пространства может быть важной в тех случаях, когда вы платите за чтение / обработку байта.
RedShift

2
Еще одна интересная проблема заключается в том, что если вы полагаетесь на компонент времени, он не будет учитывать корректировки перехода на летнее время при использовании в разное время года.
Chris

4

поскольку вы не упомянули об этом, если вы используете SQL Server 2008, вы можете использовать тип данных time, иначе использовать минуты с полуночи


3

SQL Server фактически хранит время в виде долей дня. Например, 1 целый день = значение 1. 12 часов - это значение 0,5.

Если вы хотите сохранить значение времени без использования типа DATETIME, сохранение времени в десятичной форме подойдет для этого, а также упростит преобразование в DATETIME.

Например:

SELECT CAST(0.5 AS DATETIME)
--1900-01-01 12:00:00.000

Для сохранения значения в формате DECIMAL (9,9) потребуется 5 байтов. Однако, если точность не имеет первостепенного значения, REAL будет занимать только 4 байта. В любом случае совокупный расчет (т.е. среднее время) может быть легко рассчитан для числовых значений, но не для типов данных / времени.


«SQL Server на самом деле хранит время как доли дня». Я думаю, что он хранит дни с (или раньше) 01 января 1900 года в первых 4 байтах, а время в миллисекундах - во вторых 4 байтах. (SmallDateTime использует 2 байта для каждого с более узким диапазоном дат и минуты, а не миллисекунды для времени)
Кристен,

Я знаю (уверен на 99,99%), что для времени суток это дроби.
graham.reeds

2

Я бы преобразовал их в целое число (ЧЧ * 3600 + ММ * 60) и сохранил бы его таким образом. Небольшой размер хранилища, но с ним достаточно легко работать.


2

Если вы используете MySQL, используйте тип поля TIME и связанные функции, которые поставляются с TIME.

00:00:00 - стандартный формат времени unix.

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


1

Попробуйте smalldatetime. Он может не дать вам того, что вы хотите, но он поможет вам в ваших будущих потребностях в манипуляциях с датой / временем.


если бы @mdresser использовал <MSSQL 2005, сервер должен был бы получить «значение smalldatetime вне допустимого диапазона»
Фергус

1

Вы уверены, что вам когда-нибудь понадобятся только часы и минуты? Если вы хотите сделать с ним что-нибудь значимое (например, вычислить промежутки времени между двумя такими точками данных), отсутствие информации о часовых поясах и летнем времени может дать неверные результаты. Часовые пояса, возможно, не применяются в вашем случае, но летнее время, безусловно, будет.


1

Вместо минут после полуночи мы сохраняем это как 24-часовые часы, как SMALLINT.

09:12 = 912 14:15 = 1415

при обратном преобразовании в "удобочитаемую форму" мы просто вставляем двоеточие ":" два символа справа. Если нужно, введите нули влево. Сохраняет математические данные в каждом случае и использует немного меньше байтов (по сравнению с varchar), а также обеспечивает, чтобы значение было числовым (а не буквенно-цифровым)

Довольно глупо ... в MS SQL уже много лет должен был быть тип данных TIME ИМХО ...


Кристен абсолютно права, но только если ее предположение о часах и минутах, представляющих только один 24-часовой период, верно. Для хранения 0..1439 минут требуется 10 бит. Это означает, что есть еще 6 бит, которые можно использовать по своему усмотрению, так что есть место для творчества. Я бы предложил использовать 5 бит для хранения часового смещения для часового пояса, в котором вы находитесь, но только если запрашивающий хочет сохранить максимальное время 23:59 (от одной минуты до полуночи)
WonderWorker

1

Я думаю, вы просите переменную, которая будет хранить минуты в виде числа. Это можно сделать с помощью различных типов целочисленных переменных:

SELECT 9823754987598 AS MinutesInput

Затем в своей программе вы можете просто просмотреть это в желаемой форме, вычислив:

long MinutesInAnHour = 60;

long MinutesInADay = MinutesInAnHour * 24;

long MinutesInAWeek = MinutesInADay * 7;


long MinutesCalc = long.Parse(rdr["MinutesInput"].toString()); //BigInt converts to long. rdr is an SqlDataReader.   


long Weeks = MinutesCalc / MinutesInAWeek;

MinutesCalc -= Weeks * MinutesInAWeek;


long Days = MinutesCalc / MinutesInADay;

MinutesCalc -= Days * MinutesInADay;


long Hours = MinutesCalc / MinutesInAnHour;

MinutesCalc -= Hours * MinutesInAnHour;


long Minutes = MinutesCalc;

Проблема возникает, когда вы запрашиваете эффективность использования. Но если у вас мало времени, просто используйте BigInt, допускающий значение NULL, для хранения значения минут.

Значение null означает, что время еще не было записано.

Теперь я объясню это в форме кругосветного путешествия в космос.

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

Например:

  • Если MinutesInput = 0 .. 255, используйте TinyInt (преобразовать, как описано выше).

  • Если MinutesInput = 256 .. 131071, тогда используйте SmallInt (Примечание: минимальное значение SmallInt равно -32 768. Следовательно, отрисуйте и добавьте 32768 при сохранении и извлечении значения, чтобы использовать полный диапазон перед преобразованием, как указано выше).

  • Если MinutesInput = 131072 .. 8589934591, используйте Int (примечание: отмените значение и добавьте 2147483648 при необходимости).

  • Если MinutesInput = 8589934592 .. 36893488147419103231, используйте BigInt (примечание: при необходимости добавьте и отрицайте 9223372036854775808).

  • Если MinutesInput> 36893488147419103231, я бы лично использовал VARCHAR (X), увеличивая X по мере необходимости, поскольку char - это байт. Мне придется вернуться к этому ответу позже, чтобы описать это полностью (или, может быть, другой stackoverflowee сможет закончить этот ответ).

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

Пока сохраняемые значения не достигнут числа, превышающего 36893488147419103231, у вас также может быть один столбец BigInt для представления ваших минут, поскольку вам не нужно тратить Int на уникальный идентификатор и другой int для хранения значения минут.


0

Как предложила Кристен, экономия времени в формате UTC может помочь лучше.

Убедитесь, что вы используете 24-часовые часы, потому что в UTC не используется меридиан AM или PM.

Пример:

  • 4:12 - 04:12
  • 10:12 - 1012
  • 14:28 - 1428
  • 23:56 - 2356

По-прежнему предпочтительнее использовать стандартный четырехзначный формат.


0

Сохраните ticksкак long/ bigint, которые в настоящее время измеряются в миллисекундах. Обновленное значение можно найти, посмотрев на TimeSpan.TicksPerSecondзначение.

Большинство баз данных имеют тип DateTime, который автоматически сохраняет время в виде тиков за кулисами, но в случае некоторых баз данных, например SqlLite, хранение тиков может быть способом сохранения даты.

Большинство языков позволяют легко преобразовать TicksTimeSpanTicks.

пример

В C # код будет:

long TimeAsTicks = TimeAsTimeSpan.Ticks;

TimeAsTimeSpan = TimeSpan.FromTicks(TimeAsTicks);

Однако имейте в виду, потому что в случае с SqlLite, который предлагает лишь небольшое количество различных типов: INT, REALи VARCHARнеобходимо будет сохранить количество тактов в виде строки или двух INTвместе взятых ячеек. Это потому, что это INT32-битное число BIGINTсо знаком, тогда как это 64- битное число со знаком.

Заметка

Однако лично я предпочитаю хранить дату и время в виде ISO8601строки.


-1

ИМХО, какое лучшее решение в некоторой степени зависит от того, как вы храните время в остальной части базы данных (и остальной части вашего приложения)

Лично я работал с SQLite и стараюсь всегда использовать временные метки unix для хранения абсолютного времени, поэтому, имея дело с временем дня (как вы просите), я делаю то, что Глен Солсберри пишет в своем ответе, и сохраняю количество секунд с полуночи

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.