Najlepszy sposób na przechowywanie czasu (gg: mm) w bazie danych

101

Chcę przechowywać czasy w tabeli bazy danych, ale muszę przechowywać tylko godziny i minuty. Wiem, że mogę po prostu użyć DATETIME i zignorować inne składniki daty, ale jaki jest najlepszy sposób, aby to zrobić bez przechowywania większej ilości informacji, niż faktycznie potrzebuję?

Matthew Dresser
źródło
Jaki jest odpowiednik CZASU dla serwera sql 2005?
Erran Morad
@BoratSagdiyev nie ma takiego w SQL Server 2005, dlatego zadałem to pytanie.
Matthew Dresser

Odpowiedzi:

135

Możesz zapisać to jako liczbę całkowitą określającą liczbę minut po północy:

na przykład.

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

Musiałbyś jednak napisać jakiś kod, aby odtworzyć czas, ale to nie powinno być trudne.

BigJump
źródło
8
Następnie użyj DATEADD (), aby odzyskać rzeczywiste czasy. Wystarczyłby nawet mały.
Joel Coehoorn
36
byłem tam, zrobiłem, że ... mins = dd% 60 i hours = dd / 60 na ints załatwia sprawę.
Osama Al-Maadeed
2
To świetny, prosty i nieskomplikowany pomysł. +1
whiskeysierra
7
małą wadą jest brak czytelności w bazie danych
Jowen
musimy przechowywać różne warianty dni, godzin i minut; typ danych Time w SQL Server rośnie tylko do 23:59:59, więc nie mogliśmy tego użyć. Zainspirowani Twoją odpowiedzią zdecydowaliśmy się na trzy kolumny int dla dni: godziny: minuty, aby zapewnić maksymalną elastyczność. dzięki!
matao
55

Jeśli używasz SQL Server 2008+, rozważ TIMEtyp danych. Artykuł SQLTeam zawierający więcej przykładów użycia.

WebMatrix
źródło
14

DATETIME początek DATETIME koniec

Błagam, aby zamiast tego użyć dwóch wartości DATETIME , oznaczonych czymś w rodzaju event_start i event_end .

Czas to skomplikowana sprawa

Większość świata przyjęła obecnie system metryczny oparty na denery dla większości pomiarów, słusznie lub nie. Ogólnie jest to dobre, bo przynajmniej wszyscy możemy się zgodzić, że ag, to ml, to cm sześcienny. Przynajmniej mniej więcej tak. System metryczny ma wiele wad, ale przynajmniej jest konsekwentnie błędny na całym świecie.

Z czasem jednak mamy; 1000 milisekund na sekundę, 60 sekund do minuty, 60 minut do godziny, 12 godzin na każde pół dnia, około 30 dni w miesiącu, które różnią się w zależności od miesiąca, a nawet roku, każdy kraj ma swój czas od innych , sposób formatowania czasu w każdym kraju jest różny.

To dużo do przetrawienia, ale długa i krótka to niemożliwe, aby tak złożony scenariusz miał proste rozwiązanie.

Niektóre rogi można wyciąć, ale są takie, w których rozsądniej jest tego nie robić

Chociaż górna odpowiedź sugeruje, że zapisywanie liczby całkowitej minut po północy może wydawać się całkiem rozsądne, nauczyłem się tego unikać na własnej skórze.

Powody wprowadzenia dwóch wartości DATETIME to zwiększenie dokładności, rozdzielczości i sprzężenia zwrotnego.

Są one bardzo przydatne, gdy projekt daje niepożądane rezultaty.

Czy przechowuję więcej danych niż jest to wymagane?

Początkowo może się wydawać, że przechowywanych jest więcej informacji niż potrzebuję, ale jest dobry powód, aby przyjąć to trafienie.

Przechowywanie tych dodatkowych informacji prawie zawsze oszczędza mi czas i wysiłek na dłuższą metę, ponieważ nieuchronnie stwierdzam, że gdy ktoś mówi, ile czasu zajęło, będzie chciał wiedzieć, kiedy i gdzie miało miejsce wydarzenie.

To ogromna planeta

W przeszłości byłem winny ignorowania faktu, że oprócz mojego własnego są inne kraje na tej planecie. Wtedy wydawało się to dobrym pomysłem, ale to ZAWSZE powodowało problemy, bóle głowy i późniejszą stratę czasu. ZAWSZE bierz pod uwagę wszystkie strefy czasowe.

DO#

DateTime ładnie renderuje się do ciągu w C #. Metoda ToString (format ciągu znaków) jest kompaktowa i łatwa do odczytania.

Na przykład

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

Serwer SQL

Ponadto, jeśli czytasz bazę danych oddzielnie dla interfejsu aplikacji, dateTimes jest przyjemny do odczytania na pierwszy rzut oka, a wykonywanie na nich obliczeń jest proste.

Na przykład

SELECT DATEDIFF(MINUTE, event_start, event_end)

Standard daty ISO8601

Jeśli używasz SQLite, nie masz tego, więc zamiast tego użyj pola tekstowego i zapisz je w formacie ISO8601, np.

„2013-01-27T12: 30: 00 + 0000”

Uwagi:

  • Używa 24-godzinnego zegara *

  • Przesunięcie czasu (lub +0000) części ISO8601 jest odwzorowywane bezpośrednio na wartość długości geograficznej współrzędnej GPS (nie biorąc pod uwagę czasu letniego lub całego kraju).

Na przykład

TimeOffset=(±Longitude.24)/360 

... gdzie ± odnosi się do kierunku wschodniego lub zachodniego.

Dlatego warto zastanowić się, czy warto przechowywać razem z danymi długość i szerokość geograficzną oraz wysokość. Zależy to od zastosowania.

  • ISO8601 to format międzynarodowy.

  • Więcej szczegółów można znaleźć na wiki, pod adresem http://en.wikipedia.org/wiki/ISO_8601 .

  • Data i czas są przechowywane w czasie międzynarodowym, a przesunięcie jest rejestrowane w zależności od tego, gdzie na świecie czas został zapisany.

Z mojego doświadczenia wynika, że ​​zawsze istnieje potrzeba przechowywania pełnej daty i godziny, niezależnie od tego, czy myślę, że jest to moment rozpoczęcia projektu. ISO8601 to bardzo dobry, przyszłościowy sposób na zrobienie tego.

Dodatkowe porady gratis

Warto też pogrupować wydarzenia jak łańcuch. Np. Jeśli nagrywasz wyścig, całe zdarzenie może być pogrupowane według wyścigów, wyścigów, punktów kontrolnych obwodu i okrążeń.

Z mojego doświadczenia wynika również, że mądrze jest określić, kto przechował zapis. Albo jako oddzielna tabela wypełniana przez wyzwalacz lub jako dodatkowa kolumna w oryginalnej tabeli.

Im więcej w to wkładasz, tym więcej wychodzisz

W pełni rozumiem pragnienie oszczędzania przestrzeni, jak to tylko możliwe, ale rzadko robiłbym to kosztem utraty informacji.

Praktyczna zasada dotycząca baz danych jest taka, jak mówi tytuł, baza danych może powiedzieć tylko tyle, ile zawiera dane, a cofanie się do danych historycznych i wypełnianie luk może być bardzo kosztowne.

Rozwiązaniem jest poprawienie tego za pierwszym razem. Z pewnością łatwiej to powiedzieć niż zrobić, ale teraz powinieneś mieć głębszy wgląd w efektywny projekt bazy danych, a następnie mieć znacznie większą szansę na poprawienie go za pierwszym razem.

Im lepszy projekt początkowy, tym mniej kosztowne będą późniejsze naprawy.

Mówię to wszystko tylko dlatego, że gdybym mógł cofnąć się w czasie, to właśnie to powiedziałbym sobie, kiedy tam dotarłem.

WonderWorker
źródło
2
Twój komentarz „Część +0000 ISO8601 odwzorowuje bezpośrednio szerokość geograficzną we współrzędnej GPS” jest błędna. Reprezentuje przesunięcie względem UTC
Gary Walker
po prostu zbyt rozwlekły i nie wiem, jak to w ogóle odpowiada na to pytanie. OP chce zapisać godzinę w rodzaju „8:30 rano”… dlaczego miałby do tego potrzebować 2 pola daty i godziny? W ogóle 2 pola? Wydaje mi się, że twoja odpowiedź stała się osobistą refleksją na temat tego, jak strefy czasowe są trudne w oprogramowaniu i przypadkowych innych rad dotyczących DB.
Don Cheadle
10

Po prostu przechowuj zwykłą datę i godzinę i ignoruj ​​wszystko inne. Po co poświęcać więcej czasu na pisanie kodu, który ładuje int, manipuluje nim i konwertuje go na datę i godzinę, skoro można po prostu załadować datę i godzinę?

Seth
źródło
3
Jednym z możliwych powodów może być oszczędność miejsca na dysku twardym - DATETIMEprzechowywanie typu danych zajmuje 4 bajty, podczas gdy SMALLINTna przykład zajmuje tylko jedną czwartą tego. Nie ma dużej różnicy, jeśli masz tylko kilka tysięcy wierszy, ale jeśli masz wiele milionów wierszy, jak wiele firm, oszczędność miejsca będzie znaczna.
Sheridan
32
Być może, ale weź pod uwagę, że 12 osób odpowiedziało na to pytanie, a każda z nich potrzebuje, powiedzmy, 15 minut na przemyślenie tego. Załóżmy, że wszyscy są programistami i zarabiają 50 dolarów na godzinę. Koszt czasu spędzonego tylko na przemyśleniu tego problemu mógłbyś kupić nowy błyszczący dysk twardy 2 TB do przechowywania dodatkowych bajtów.
Seth
@ Masz rację, jeśli mówimy tylko o dodatkowych bajtach. Ale twoja sugestia może być tylko wtedy, gdy jest to mały projekt z 1-2 programistami. Ale będzie to duży projekt z wieloma programistami (plus QA), byłby to duży problem, gdyby nowy programista próbował to rozgryźć. Niestety wszyscy polegamy na logice biznesowej.
Mediator
W przypadku usług w chmurze oszczędność miejsca na dysku może być ważna w przypadkach, gdy płacisz za odczyt / przetworzenie bajtu.
RedShift
2
Innym zabawnym problemem jest to, że jeśli polegasz na składniku czasu, nie będzie on uwzględniał korekt czasu letniego, gdy jest używany w różnych porach roku.
Chris Seufert
4

ponieważ nie wspomniałeś o tym trochę, jeśli korzystasz z SQL Server 2008, możesz użyć typu danych czas, w przeciwnym razie użyj minut od północy

SQLMenace
źródło
3

SQL Server faktycznie przechowuje czas jako ułamki dnia. Na przykład 1 cały dzień = wartość 1. 12 godzin to wartość 0,5.

Jeśli chcesz przechowywać wartość czasu bez użycia typu DATETIME, przechowywanie czasu w postaci dziesiętnej będzie odpowiadało tej potrzebie, jednocześnie ułatwiając konwersję na DATETIME.

Na przykład:

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

Zapisanie wartości jako DECIMAL (9,9) zajęłoby 5 bajtów. Jednak jeśli precyzja nie ma największego znaczenia, RZECZYWISTA zajęłaby tylko 4 bajty. W obu przypadkach łączne obliczenia (tj. Średni czas) można łatwo obliczyć na podstawie wartości liczbowych, ale nie na typach danych / czasu.

Taylor Gerring
źródło
„SQL Server faktycznie przechowuje czas jako ułamki dnia”. Myślę, że przechowuje dni od (lub wcześniej) 1 stycznia 1900 r. W pierwszych 4 bajtach i czasie, w milisekundach, w drugich 4 bajtach. (SmallDateTime wykorzystuje 2 bajty dla każdego z węższym zakresem dat i minut, a nie milisekundami dla czasu)
Kristen,
Wiem (na 99,99% pewności), że dla pory dnia są to ułamki.
graham.reeds
2

Zamieniłbym je na liczbę całkowitą (HH * 3600 + MM * 60) i zapisałbym w ten sposób. Mały rozmiar pamięci masowej i nadal wystarczająco łatwy w obsłudze.

Glen Solsberry
źródło
2

Jeśli używasz MySQL, użyj typu pola TIME i powiązanej funkcjonalności, która jest dostarczana z TIME.

00:00:00 to standardowy format czasu w systemie Unix.

Jeśli kiedykolwiek będziesz musiał spojrzeć wstecz i przejrzeć tabele ręcznie, liczby całkowite mogą być bardziej zagmatwane niż rzeczywisty znacznik czasu.

Składnia
źródło
1

Wypróbuj smalldatetime. Może nie dać ci tego, czego chcesz, ale pomoże ci w twoich przyszłych potrzebach w zakresie manipulacji datą / godziną.

MarlonRibunal
źródło
jeśli @mdresser używał <MSSQL 2005, serwer przekroczyłby „małą wartość poza zakresem”
Fergus,
1

Czy na pewno będziesz potrzebować tylko godzin i minut? Jeśli chcesz zrobić z nim coś sensownego (na przykład obliczyć okresy między dwoma takimi punktami danych), brak informacji o strefach czasowych i czasie letnim może dać nieprawidłowe wyniki. Strefy czasowe może nie mają zastosowania w twoim przypadku, ale czas letni z pewnością tak.

mghie
źródło
1

Zamiast minut po północy przechowujemy go jako zegar 24-godzinny, jako SMALLINT.

09:12 = 912 14:15 = 1415

podczas konwersji z powrotem do „postaci czytelnej dla człowieka” po prostu wstawiamy dwukropek „:” dwa znaki z prawej strony. W razie potrzeby dopełnij lewymi zerami. Zapisuje matematykę w obie strony i wykorzystuje kilka mniej bajtów (w porównaniu z varchar), a także wymusza, aby wartość była numeryczna (a nie alfanumeryczna)

Dość głupio ... w MS SQL powinien istnieć typ danych TIME już od wielu lat IMHO ...

Kristen
źródło
Kristen ma całkowitą rację, ale tylko wtedy, gdy jej założenie dotyczące godzin i minut reprezentujących tylko jeden 24-godzinny okres jest poprawne. Do zapamiętania 0..1439 minut potrzeba 10 bitów. Oznacza to, że istnieje dodatkowe 6 bitów, których można używać w dowolny sposób, więc jest miejsce na kreatywność. Sugerowałbym użycie 5 bitów do przechowywania przesunięcia godzinowego dla strefy czasowej, w której się znajdujesz, ale tylko wtedy, gdy pytający chciał zapisać maksymalny czas 23:59 (jedna minuta do północy)
WonderWorker
1

Myślę, że pytasz o zmienną, która będzie przechowywać minuty jako liczbę. Można to zrobić za pomocą różnych typów zmiennych całkowitych:

SELECT 9823754987598 AS MinutesInput

Następnie w swoim programie możesz po prostu wyświetlić to w żądanej formie, obliczając:

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;

Problem pojawia się, gdy prosisz o wykorzystanie wydajności. Ale jeśli masz mało czasu, to prostu użyj wartości zerowej BigInt do przechowywania wartości minut.

Wartość null oznacza, że ​​czas nie został jeszcze zarejestrowany.

Teraz wyjaśnię w formie podróży w obie strony do kosmosu.

Niestety kolumna tabeli będzie przechowywać tylko jeden typ. W związku z tym konieczne będzie utworzenie nowej tabeli dla każdego typu, zgodnie z wymaganiami.

Na przykład:

  • Jeśli MinutesInput = 0 ... 255, użyj TinyInt (Konwertuj zgodnie z opisem powyżej).

  • Jeśli MinutesInput = 256 .. 131071, użyj SmallInt (Uwaga: minimalna wartość SmallInt to -32 768. Dlatego zaneguj i dodaj 32768 podczas przechowywania i pobierania wartości, aby wykorzystać pełny zakres przed konwersją jak powyżej).

  • Jeśli MinutesInput = 131072 .. 8589934591, użyj wartości Int (Uwaga: w razie potrzeby dodaj wartość ujemną i 2147483648).

  • Jeśli MinutesInput = 8589934592 .. 36893488147419103231, użyj BigInt (Uwaga: w razie potrzeby dodaj i zaneguj 9223372036854775808 ).

  • Jeśli MinutesInput> 36893488147419103231, wtedy osobiście użyłbym VARCHAR (X) zwiększając X w razie potrzeby, ponieważ znak jest bajtem. Będę musiał ponownie przejrzeć tę odpowiedź w późniejszym terminie, aby opisać to w całości (lub może inny znajomy stackoverflowee może dokończyć tę odpowiedź).

Ponieważ każda wartość będzie niewątpliwie wymagać unikalnego klucza, wydajność bazy danych będzie widoczna tylko wtedy, gdy zakres przechowywanych wartości będzie dobrym połączeniem między bardzo małym (blisko 0 minut) i bardzo wysokim (większym niż 8589934591).

Dopóki przechowywane wartości nie osiągną liczby większej niż 36893488147419103231, równie dobrze możesz mieć pojedynczą kolumnę BigInt do reprezentowania minut, ponieważ nie będziesz musiał marnować wartości Int na unikalny identyfikator i inną liczbę int do przechowywania wartości minut.

WonderWorker
źródło
0

Oszczędność czasu w formacie UTC może pomóc lepiej, jak zasugerowała Kristen.

Upewnij się, że korzystasz z zegara 24-godzinnego, ponieważ w UTC nie ma południka AM ani PM.

Przykład:

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

Nadal preferowane jest użycie standardowego czterocyfrowego formatu.

balamurugavel
źródło
0

Zapisz ticksjako long/ bigint, które są obecnie mierzone w milisekundach. Zaktualizowaną wartość można znaleźć, patrząc na TimeSpan.TicksPerSecondwartość.

Większość baz danych ma typ DateTime, który automatycznie przechowuje czas jako ticky za kulisami, ale w przypadku niektórych baz danych, np. SqlLite, przechowywanie ticków może być sposobem na przechowywanie daty.

Większość języków umożliwia łatwą konwersję z TicksTimeSpanTicks.

Przykład

W C # kod wyglądałby tak:

long TimeAsTicks = TimeAsTimeSpan.Ticks;

TimeAsTimeSpan = TimeSpan.FromTicks(TimeAsTicks);

Należy jednak pamiętać, ponieważ w przypadku SqlLite, który oferuje tylko niewielką liczbę różnych typów, którymi są; INT, REALI VARCHARbędzie konieczne zachowanie numeru kleszczy jako ciąg dwóch lub INTkomórek łącznie. Dzieje się tak, ponieważ INTjest to 32-bitowy numer ze BIGINTznakiem, podczas gdy jest to 64 -bitowy numer ze znakiem.

Uwaga

Jednak moim osobistym preferencją byłoby przechowywanie daty i godziny jako ISO8601ciągu.

WonderWorker
źródło
-1

IMHO to, które rozwiązanie jest najlepsze, zależy w pewnym stopniu od tego, jak przechowujesz czas w pozostałej części bazy danych (i reszcie aplikacji)

Osobiście pracowałem z SQLite i staram się zawsze używać uniksowych znaczników czasu do przechowywania czasu absolutnego, więc gdy mam do czynienia z porą dnia (tak jak prosisz) robię to, co Glen Solsberry pisze w swojej odpowiedzi i przechowuję liczbę sekund od północy

Przyjmując to ogólne podejście, ludzie (w tym ja!) Czytający kod są mniej zdezorientowani, jeśli wszędzie używam tego samego standardu

sunyata
źródło