Il modo migliore per memorizzare l'ora (hh: mm) in un database


100

Voglio memorizzare gli orari in una tabella di database ma devo solo memorizzare ore e minuti. So che potrei semplicemente usare DATETIME e ignorare gli altri componenti della data, ma qual è il modo migliore per farlo senza memorizzare più informazioni di quelle effettivamente necessarie?


Qual è l'equivalente di TIME per sql server 2005?
Erran Morad

@ BoratSagdiyev non ce n'è uno in SQL Server 2005, ecco perché ho posto la domanda.
Matthew Dresser

Risposte:


135

Puoi memorizzarlo come numero intero del numero di minuti dopo la mezzanotte:

per esempio.

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

Tuttavia, dovresti scrivere del codice per ricostituire l'ora, ma non dovrebbe essere complicato.


8
Quindi usa DATEADD () per recuperare i tempi reali. Anche smallint sarebbe sufficiente.
Joel Coehoorn,

36
stato lì, fatto che ... min = gg% 60 e ore = gg / 60 su ints fa il trucco.
Osama Al-Maadeed

2
È un'idea fantastica, semplice e immediata. +1
whiskeysierra

7
un piccolo svantaggio è la mancanza di leggibilità nel database
Jowen

dobbiamo memorizzare alcune varianti di giorni, ore e minuti; il tipo di dati Time in SQL Server arriva solo alle 23:59:59, quindi non è stato possibile utilizzarlo. Ispirati dalla tua risposta, abbiamo deciso di avere tre colonne int per giorni: ore: min per la massima flessibilità. Grazie!
matao


15

DATETIME start DATETIME end

Ti imploro di usare invece due valori DATETIME , etichettati con qualcosa come event_start e event_end .

Il tempo è un affare complesso

La maggior parte del mondo ha ora adottato il sistema metrico basato sul denery per la maggior parte delle misurazioni, a torto oa ragione. Questo è un bene nel complesso, perché almeno possiamo essere tutti d'accordo che g, è un ml, è un cm cubo. Almeno approssimativamente così. Il sistema metrico ha molti difetti, ma almeno è costantemente imperfetto a livello internazionale.

Con il tempo, tuttavia, abbiamo; 1000 millisecondi in un secondo, da 60 secondi a un minuto, da 60 minuti a un'ora, 12 ore per ogni mezza giornata, circa 30 giorni al mese che variano in base al mese e persino all'anno in questione, ogni paese ha il suo tempo compensato dagli altri , il modo in cui l'ora è formattata in ogni paese varia.

È molto da digerire, ma il lungo e il corto è impossibile per uno scenario così complesso avere una soluzione semplice.

Alcuni angoli possono essere tagliati, ma ci sono quelli in cui è meglio non farlo

Sebbene la risposta migliore qui suggerisca che memorizzare un numero intero di minuti dopo la mezzanotte potrebbe sembrare perfettamente ragionevole, ho imparato a evitare di farlo nel modo più duro.

I motivi per implementare due valori DATETIME sono per un aumento di precisione, risoluzione e feedback.

Questi sono tutti molto utili quando il design produce risultati indesiderati.

Sto memorizzando più dati del necessario?

All'inizio potrebbe sembrare che vengano archiviate più informazioni di quelle necessarie, ma c'è una buona ragione per accettare questo colpo.

Memorizzare queste informazioni extra finisce quasi sempre per farmi risparmiare tempo e fatica a lungo termine, perché inevitabilmente scopro che quando a qualcuno viene detto quanto tempo ha impiegato qualcosa, vorranno anche sapere anche quando e dove si è verificato l'evento.

È un pianeta enorme

In passato, sono stato colpevole di ignorare che ci sono altri paesi su questo pianeta oltre al mio. All'epoca sembrava una buona idea, ma questo ha SEMPRE provocato problemi, mal di testa e perdite di tempo in seguito. Considera SEMPRE tutti i fusi orari.

C #

Un DateTime esegue correttamente il rendering di una stringa in C #. Il metodo ToString (string Format) è compatto e di facile lettura.

Per esempio

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

Server SQL

Inoltre, se stai leggendo il tuo database separatamente dall'interfaccia dell'applicazione, dateTimes è piacevole da leggere a colpo d'occhio e l'esecuzione di calcoli su di essi è semplice.

Per esempio

SELECT DATEDIFF(MINUTE, event_start, event_end)

Standard di data ISO8601

Se utilizzi SQLite, non lo hai, quindi utilizza un campo di testo e memorizzalo in formato ISO8601, ad es.

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

Appunti:

  • Utilizza l'orologio a 24 ore *

  • La parte di offset dell'ora (o +0000) della ISO8601 mappa direttamente al valore di longitudine di una coordinata GPS (senza considerare l'ora legale o in tutto il paese).

Per esempio

TimeOffset=(±Longitude.24)/360 

... dove ± si riferisce alla direzione est o ovest.

Vale quindi la pena considerare se valga la pena memorizzare longitudine, latitudine e altitudine insieme ai dati. Ciò varierà nell'applicazione.

  • ISO8601 è un formato internazionale.

  • Il wiki è molto utile per ulteriori dettagli su http://en.wikipedia.org/wiki/ISO_8601 .

  • La data e l'ora vengono memorizzate nell'ora internazionale e l'offset viene registrato a seconda di dove è stata memorizzata l'ora nel mondo.

Nella mia esperienza c'è sempre la necessità di memorizzare la data e l'ora complete, indipendentemente dal fatto che io pensi che ci sia quando inizio il progetto. ISO8601 è un ottimo modo per farlo a prova di futuro.

Ulteriori consigli gratuiti

Vale anche la pena raggruppare gli eventi insieme come una catena. Ad esempio, se si registra una gara, l'intero evento potrebbe essere raggruppato per racer, race_circuit, circuit_checkpoints e circuit_laps.

Nella mia esperienza, è anche saggio identificare chi ha archiviato il record. O come tabella separata popolata tramite trigger o come colonna aggiuntiva all'interno della tabella originale.

Più metti, più ne esci

Capisco perfettamente il desiderio di essere il più economico possibile con lo spazio, ma raramente lo farei a scapito della perdita di informazioni.

Una regola pratica con i database è come dice il titolo, un database può solo dirti quanto ha dati e può essere molto costoso tornare indietro attraverso i dati storici, colmando le lacune.

La soluzione è farlo correttamente la prima volta. Questo è certamente più facile a dirsi che a farsi, ma ora dovresti avere una visione più approfondita di una progettazione di database efficace e, di conseguenza, avere maggiori possibilità di farlo bene la prima volta.

Migliore è il tuo progetto iniziale, meno costose saranno le riparazioni successive.

Dico solo tutto questo, perché se potessi tornare indietro nel tempo, è quello che mi direi una volta arrivato.


2
Il tuo commento "La parte +0000 della ISO8601 mappa direttamente sulla latitudine in una coordinata GPS" è sbagliato. Rappresenta l' offset da UTC
Gary Walker

10

Memorizza solo un datetime regolare e ignora tutto il resto. Perché dedicare più tempo alla scrittura di codice che carica un int, lo manipola e lo converte in un datetime, quando potresti semplicemente caricare un datetime?


3
Una possibile ragione potrebbe essere quella di risparmiare spazio sul disco rigido: un DATETIMEtipo di dati richiede 4 byte per essere archiviato, mentre a, SMALLINTad esempio, ne richiede solo un quarto. Nessuna grande differenza se hai solo poche migliaia di righe, ma se hai molti milioni di righe come fanno molte aziende, il tuo risparmio di spazio sarà notevole.
Sheridan

32
Forse, ma considera che 12 persone hanno risposto a questa domanda, ciascuna impiegando, diciamo, 15 minuti per pensarci. Supponiamo che siano tutti programmatori e guadagnino $ 50 l'ora. Con il costo del tempo speso solo per pensare a questo problema, avresti potuto acquistare un nuovo disco rigido da 2 TB per memorizzare i byte extra.
Seth

@Seth hai ragione se parliamo solo di byte extra. Ma il tuo suggerimento può essere solo se si tratta di un piccolo progetto con 1-2 sviluppatori. Ma sarà un grande progetto con molti sviluppatori (più QA), sarebbe un grosso problema quando un nuovo programmatore cerca di capirlo. Purtroppo dipendiamo tutti dalla logica aziendale.
Mediatore

Con i servizi cloud, risparmiare spazio su disco può essere importante nei casi in cui si paga per byte letto / elaborato.
RedShift

2
Un altro problema divertente è che se ti affidi alla componente temporale, questa non terrà conto delle regolazioni dell'ora legale se utilizzata in periodi diversi dell'anno.
Chris Seufert

4

dato che non l'hai menzionato un po 'se sei su SQL Server 2008 puoi usare il tipo di dati time altrimenti usa i minuti dalla mezzanotte


3

SQL Server archivia effettivamente l'ora come frazioni di giorno. Ad esempio, 1 giorno intero = valore di 1. 12 ore è un valore di 0,5.

Se si desidera memorizzare il valore dell'ora senza utilizzare un tipo DATETIME, la memorizzazione dell'ora in forma decimale si adatterà a tale esigenza, semplificando anche la conversione in DATETIME.

Per esempio:

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

La memorizzazione del valore come DECIMAL (9,9) consumerebbe 5 byte. Tuttavia, se la precisione non è della massima importanza, un REAL consumerebbe solo 4 byte. In entrambi i casi, il calcolo aggregato (cioè il tempo medio) può essere facilmente calcolato su valori numerici, ma non sui tipi di dati / ora.


"SQL Server archivia effettivamente l'ora come frazioni di un giorno". Penso che memorizzi i giorni dal (o prima) 01 gennaio 1900 nei primi 4 byte e l'ora, in millisecondi, nei secondi 4 byte. (SmallDateTime utilizza 2 byte per ciascuno con un intervallo di date e minuti più ristretto, anziché millisecondi)
Kristen

So (sicuro al 99,99%) che per l'ora del giorno sono frazioni.
graham.reeds

2

Li convertirò in un numero intero (HH * 3600 + MM * 60) e lo memorizzerei in questo modo. Dimensioni di archiviazione ridotte e comunque abbastanza facile da lavorare.


2

Se stai usando MySQL, usa un tipo di campo TIME e la funzionalità associata fornita con TIME.

00:00:00 è il formato dell'ora unix standard.

Se dovessi mai guardare indietro e rivedere le tabelle a mano, i numeri interi possono creare più confusione di un effettivo timestamp.


1

Prova smalldatetime. Potrebbe non darti quello che vuoi ma ti aiuterà nelle tue esigenze future nella manipolazione di data / ora.


se @mdresser usasse <MSSQL 2005, il server avrebbe superato "valore smalldatetime fuori intervallo"
Fergus

1

Sei sicuro di aver bisogno solo delle ore e dei minuti? Se vuoi fare qualcosa di significativo con esso (come ad esempio calcolare intervalli di tempo tra due di questi punti dati) non avere informazioni sui fusi orari e l'ora legale potrebbe dare risultati errati. I fusi orari forse non si applicano nel tuo caso, ma l'ora legale lo farà sicuramente.


1

Invece di minuti dopo mezzanotte, lo memorizziamo come orologio delle 24 ore, come SMALLINT.

09:12 = 912 14:15 = 1415

quando si converte di nuovo in "forma leggibile dall'uomo", inseriamo solo due punti ":" due caratteri da destra. Tasto sinistro con zeri se necessario. Salva la matematica in ogni modo e utilizza pochi byte in meno (rispetto a varchar), inoltre impone che il valore sia numerico (anziché alfanumerico)

Piuttosto sciocco però ... ci sarebbe dovuto essere un tipo di dati TIME in MS SQL già da molti anni IMHO ...


Kristen ha assolutamente ragione, ma solo se la sua ipotesi sulle ore e sui minuti che rappresentano solo un singolo periodo di 24 ore è corretta. Sono necessari 10 bit per memorizzare 0..1439 minuti. Ciò significa che ci sono altri 6 bit che possono essere utilizzati come preferisci, quindi c'è spazio per essere creativi. Suggerirei di utilizzare 5 bit per memorizzare l'offset orario per il fuso orario in cui ti trovi, ma solo se il richiedente desidera memorizzare solo un tempo massimo di 23:59 (da un minuto a mezzanotte)
WonderWorker

1

Quello che penso tu stia chiedendo è una variabile che memorizzerà i minuti come numero. Questo può essere fatto con i diversi tipi di variabile intera:

SELECT 9823754987598 AS MinutesInput

Quindi, nel tuo programma puoi semplicemente visualizzarlo nella forma che desideri calcolando:

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;

Si verifica un problema in cui si richiede di utilizzare l'efficienza. Tuttavia, se hai poco tempo, usa un BigInt nullable per memorizzare il valore dei tuoi minuti.

Un valore null significa che l'ora non è stata ancora registrata.

Ora, spiegherò sotto forma di un viaggio di andata e ritorno nello spazio.

Sfortunatamente, una colonna di tabella memorizzerà solo un singolo tipo. Pertanto, sarà necessario creare una nuova tabella per ogni tipo in base alle esigenze.

Per esempio:

  • Se MinutesInput = 0 .. 255, usa TinyInt (Converti come descritto sopra).

  • Se MinutesInput = 256 .. 131071, utilizzare SmallInt (Nota: il valore minimo di SmallInt è -32,768. Pertanto, negare e aggiungere 32768 durante la memorizzazione e il recupero del valore per utilizzare l'intervallo completo prima della conversione come sopra).

  • Se MinutesInput = 131072 .. 8589934591, utilizzare Int (Nota: negare e aggiungere 2147483648 se necessario).

  • Se MinutesInput = 8589934592 .. 36893488147419103231, utilizza BigInt (Nota: aggiungi e annulla 9223372036854775808 secondo necessità).

  • Se MinutesInput> 36893488147419103231, userò personalmente VARCHAR (X) aumentando X se necessario poiché un char è un byte. Dovrò rivisitare questa risposta in un secondo momento per descriverlo per intero (o forse un altro stackoverflowee può finire questa risposta).

Poiché ogni valore richiederà indubbiamente una chiave univoca, l'efficienza del database sarà evidente solo se l'intervallo dei valori memorizzati è un buon mix tra molto piccolo (vicino a 0 minuti) e molto alto (maggiore di 8589934591).

Fino a quando i valori archiviati non raggiungono effettivamente un numero maggiore di 36893488147419103231, potresti anche avere una singola colonna BigInt per rappresentare i tuoi minuti, poiché non dovrai sprecare un Int su un identificatore univoco e un altro int per memorizzare il valore dei minuti.


0

Il risparmio di tempo in formato UTC può aiutare meglio come suggerito da Kristen.

Assicurati di utilizzare l'orologio a 24 ore perché non ci sono meridiani AM o PM da utilizzare in UTC.

Esempio:

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

È ancora preferibile utilizzare il formato standard a quattro cifre.


0

Memorizza tickscome long/ bigint, che sono attualmente misurati in millisecondi. Il valore aggiornato può essere trovato osservando il TimeSpan.TicksPerSecondvalore.

La maggior parte dei database ha un tipo DateTime che memorizza automaticamente l'ora come tick dietro le quinte, ma nel caso di alcuni database, ad esempio SqlLite, la memorizzazione dei tick può essere un modo per memorizzare la data.

La maggior parte delle lingue consente la facile conversione da TicksTimeSpanTicks.

Esempio

In C # il codice sarebbe:

long TimeAsTicks = TimeAsTimeSpan.Ticks;

TimeAsTimeSpan = TimeSpan.FromTicks(TimeAsTicks);

Attenzione però, perché nel caso di SqlLite, che offre solo un piccolo numero di tipi diversi, che sono; INT, REALe VARCHARsarà necessario memorizzare il numero di tick come una stringa o due INTcelle combinate. Questo perché an INTè un numero con segno a 32 bit mentre BIGINTè un numero con segno a 64 bit.

Nota

La mia preferenza personale, tuttavia, sarebbe quella di memorizzare la data e l'ora come una ISO8601stringa.


-1

IMHO quale sia la soluzione migliore dipende in una certa misura da come memorizzi il tempo nel resto del database (e nel resto della tua applicazione)

Personalmente ho lavorato con SQLite e cerco di utilizzare sempre i timestamp unix per memorizzare l'ora assoluta, quindi quando ho a che fare con l'ora del giorno (come chiedi tu) faccio quello che scrive Glen Solsberry nella sua risposta e memorizzo il numero di secondi dalla mezzanotte

Quando adotto questo approccio generale le persone (me compreso!) Che leggono il codice sono meno confuse se uso lo stesso standard ovunque

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.