Найкращий спосіб зберігати час (год: мм) у базі даних


100

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


Який еквівалент TIME для sql сервера 2005?
Ерран Морад

@BoratSagdiyev у SQL Server 2005 немає жодної, тому я задав це питання.
Меттью Комод

Відповіді:


135

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

напр.

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

Однак вам потрібно буде написати якийсь код, щоб відновити час, але це не повинно бути складним.


8
Потім використовуйте DATEADD (), щоб повернути реальний час. Навіть маленькогонеобхідно вистачити.
Joel Coehoorn

36
був там, зробив, що ... хв = dd% 60 і годин = dd / 60 на ints робить трюк.
Осама Аль-Мадейд

2
Це чудова, проста і зрозуміла ідея. +1
віскісієра

7
невеликим недоліком є ​​відсутність читабельності в базі даних
Jowen

нам потрібно зберігати якийсь варіант днів, годин і хвилин; тип даних про час у SQL Server продовжується лише до 23:59:59, тому ми не могли ним скористатися. Натхненні вашою відповіддю, ми вирішили мати три колонки в цілому протягом днів: години: хв для максимальної гнучкості. Дякую!
матао


15

DATETIME start DATETIME end

Я закликаю вас замість цього використовувати два значення 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, у вас цього немає, тому замість цього використовуйте поле Text та зберігайте його у форматі ISO8601, наприклад.

"2013-01-27T12: 30: 00 + 0000"

Примітки:

  • Для цього використовується цілодобовий годинник *

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

Напр

TimeOffset=(±Longitude.24)/360 

... де ± відноситься до східного або західного напрямку.

Тому варто подумати, чи варто зберігати довготу, широту та висоту разом із даними. Це залежатиме від застосування.

  • ISO8601 - це міжнародний формат.

  • Вікі дуже хороша для отримання детальної інформації на веб-сайті http://en.wikipedia.org/wiki/ISO_8601 .

  • Дата та час зберігаються у міжнародному часі, а зміщення записується залежно від того, де у світі зберігався час.

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

Додаткова порада безкоштовно

Також варто згрупувати події разом, як ланцюжок. Наприклад, якщо записати гонку, вся подія може бути згрупована за участю гонщика, race_circuit, krug_checkpoints та krug_laps.

На мій досвід, також розумно визначити, хто зберігав запис. Або як окрема таблиця, заповнена через тригер, або як додаткова колонка в початковій таблиці.

Чим більше ви вкладаєте, тим більше виходите

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

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

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

Чим краще ваш початковий проект, тим менше коштують ремонти пізніше.

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


2
Ваш коментар "+0000 частина ISO8601 відображається безпосередньо на широту в GPS-координаторі" неправильна. Він представляє зсув від UTC
Гарі Уокер

10

Просто зберігайте звичайну дату і ігноруйте все інше. Навіщо витрачати додатковий час на написання коду, який завантажує int, маніпулює ним і перетворює його в datetime, коли ви можете просто завантажити дату?


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

32
Можливо, але врахуйте, що 12 людей відповіли на це запитання, кожен зайнявши, скажімо, 15 хвилин, щоб подумати над цим. Припустимо, що вони всі програмісти і заробляють 50 доларів на годину. З витратою часу, витраченого лише на роздуми над цією проблемою, ви могли придбати новий блискучий жорсткий диск на 2 Тб для зберігання зайвих байтів.
Сет

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

За допомогою хмарних сервісів економія місця на диску може бути важливою у випадках, коли ви платите за прочитаний / оброблений байт.
RedShift

2
Ще одна цікава проблема - якщо ви покладаєтесь на часовий компонент, він не буде враховувати коригування літнього часу при використанні в різний час року.
Кріс Сеферт

4

оскільки ви не згадали про це, якщо ви перебуваєте на SQL Server 2008, ви можете використовувати тип даних часу, інакше використовувати хвилини з півночі


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

Я перетворив би їх на ціле число (HH * 3600 + MM * 60) і збереже його таким чином. Невеликий обсяг пам’яті та все ще досить простий у роботі.


2

Якщо ви використовуєте MySQL, використовуйте тип поля TIME та пов’язану з ним функціональність, що постачається з TIME.

00:00:00 - стандартний формат часу Unix.

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


1

Спробуйте smalldatetime. Він може не дати тобі того, чого ти хочеш, але це допоможе тобі у майбутніх потребах у маніпуляціях з датою та часом.


якби @mdresser використовував <MSSQL 2005, сервер пронизував би значення "поза межами діапазону smalldatetime"
Фергюс

1

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


1

Замість хвилин минулої півночі ми зберігаємо це як годинник 24 години, як МАЛИЙ.

09:12 = 912 14:15 = 1415

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

Досить гуфі, хоча ... у MS SQL вже багато років повинен був існувати тип даних TIME, IMHO ...


Крістен абсолютно правильна, але лише в тому випадку, якщо її припущення про години та хвилини, що представляють собою лише один 24-годинний період, є правильним. 10 біт потрібно для зберігання 0..1439 хвилин. Це означає, що є додаткові 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 означає, що час ще не записано.

Тепер я поясню у формі зворотного подорожі до космосу.

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

Наприклад:

  • Якщо 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 за необхідності, оскільки знаком є ​​байт. Мені доведеться переглянути цю відповідь пізніше, щоб описати це в повному обсязі (або, можливо, співрозмовник стацковерфей може закінчити цю відповідь).

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

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


0

Економія часу у форматі UTC може допомогти краще, як запропонувала Крістен.

Переконайтеся, що ви використовуєте годинник 24 години, оскільки в UTC немає меридіана AM або PM.

Приклад:

  • 04:12 - 0412
  • 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

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

Особисто я працював із SQLite і намагаюся завжди використовувати часові позначки unix для зберігання абсолютного часу, тому, маючи справу з часом доби (як ви просите), я роблю те, що пише Glen Solsberry у своїй відповіді, і зберігаю кількість секунд з півночі

При застосуванні цього загального підходу люди (включаючи мене!) Читання коду менше плутаються, якщо я всюди використовую один і той же стандарт

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