Modo preferito per memorizzare DateTime


18

Siamo in grado di memorizzare le informazioni su data e ora in un paio di modi. Qual è l'approccio migliore per la memorizzazione delle informazioni di DateTime?

Memorizzare data e ora in 2 colonne separate o una colonna usando DateTime ?

Puoi spiegare perché questo approccio è migliore?

(Collegamento a documenti MySQL per riferimento, la domanda è generale, non specifica per MySQL)
Tipi di data e ora : Data e ora


3
Ciò dipende in gran parte dal sistema di database in uso. Per quello che vale: Oracle ha scelto di farlo come una colonna (come tipo di dati DATETIME), nel qual caso, l'utilizzo del loro supporto integrato sarà sicuramente superiore rispetto alla memorizzazione di tali informazioni in 2 colonne come NUMBER tipi di dati (anche se solo tu bisogno di 1 parte per una determinata query ... la data o l'ora).
Kris Johnston,

5
Per SQL Server un caso in cui è possibile preferire la suddivisione è il raggruppamento per data. Un aggregato di flusso sarà in grado di essere utilizzato senza un ordinamento per l'indice composito date,time con group by datema non per un indice datetime con group by cast(datetime as date)anche se fornirebbe l'ordine desiderato.
Martin Smith,

1
Si noti che qualsiasi calcolo matematico sui valori di tempo richiede la conoscenza della data e del fuso orario, ad esempio la distanza tra due volte dipende dal fatto che quel giorno contenga un evento DST, alcuni giorni abbiano 23 o 25 ore e esistano anche dei secondi bisestili.
Peteris,

Risposte:


23

La memorizzazione dei dati in una singola colonna è il modo preferito, poiché sono indissolubilmente collegati. Un punto nel tempo è una singola informazione, non due.

Un modo comune di memorizzare i dati di data / ora, utilizzati "dietro le quinte" da molti prodotti, è convertirli in un valore decimale in cui la "data" è la parte intera del valore decimale e il "tempo" è il frazionario valore. Quindi, 1900-01-01 00:00:00 viene archiviato come 0,0 e il 20 settembre 2016 9:34:00 viene archiviato come 42631.39861. 42631 è il numero di giorni dal 1900-01-01. .39861 è la parte di tempo trascorsa dalla mezzanotte. Non utilizzare direttamente un tipo decimale per fare ciò, utilizzare un tipo di data / ora esplicito; il mio punto qui è solo un'illustrazione.

Memorizzare i dati in due colonne separate significa che dovrai combinare entrambi i valori di colonna ogni volta che vuoi vedere se un dato momento è precedente o successivo al valore memorizzato.

Se memorizzi i valori separatamente, invariabilmente ti imbatterai in "bug" che sono difficili da rilevare. Prendi ad esempio quanto segue:

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');

Nel codice sopra, stiamo creando una tabella di test, popolandola con due valori, quindi eseguendo una semplice query su tali dati. Il primo SELECTrestituisce entrambe le righe, tuttavia il secondo SELECTrestituisce solo una singola riga, che potrebbe non essere il risultato desiderato:

inserisci qui la descrizione dell'immagine

Il modo corretto di filtrare un intervallo di data / ora in cui i valori sono in colonne discrete, come sottolineato da @ypercube nei commenti, è:

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')
    )

Se è necessario separare il componente temporale ai fini dell'analisi , è possibile prendere in considerazione l'aggiunta di una colonna calcolata, persistente, per la parte temporale del valore:

ALTER TABLE #DT
ADD dt_value_time AS CONVERT(TIME(0), dt_value) PERSISTED;

SELECT *
FROM #dt;

inserisci qui la descrizione dell'immagine

La colonna persistente potrebbe quindi essere indicizzata consentendo ordinamenti veloci, ecc. Per ora del giorno.

Se stai pensando di dividere la data e l'ora in due campi per scopi di visualizzazione, dovresti capire che la formattazione dovrebbe essere fatta sul client, non sul server.


11

Fornirò un'opinione dissenziente alle altre risposte.

Se entrambi i componenti data e ora sono richiesti insieme, ovvero una voce non è valida se contiene uno ma non l'altro (o è NULL in uno ma non nell'altro), la memorizzazione in una singola colonna ha senso per i motivi indicati in altri risposte.

Tuttavia, è possibile che uno o entrambi i componenti siano individualmente opzionali. In tal caso, non sarebbe corretto memorizzarlo in una singola colonna. Ciò ti costringerebbe a rappresentare i valori NULL in modo arbitrario, ad esempio memorizzando l'ora come 00:00:00.

Qui ci sono un paio di esempi:

  • Stai registrando i viaggi del veicolo per le detrazioni fiscali di chilometraggio. Conoscere l'ora esatta del viaggio sarebbe utile ma se un dipendente non l'ha annotato e se ne è dimenticato, la data dovrebbe essere comunque registrata da sola (data richiesta, ora facoltativa).

  • Stai conducendo un sondaggio per scoprire a che ora le persone mangiano il loro pranzo e chiedi ai partecipanti di compilare un modulo con un campione delle loro ore di pranzo, comprese le date. Alcuni non si preoccupano di compilare la data e non vuoi scartare i dati poiché sono le ore a cui tieni veramente (data opzionale, ora richiesta).

Vedi questa domanda correlata per approcci alternativi.


In RFC 3339 esiste una convenzione per la registrazione di "offset locale sconosciuto". Non penso che copra abbastanza il caso d'uso di "tempo sconosciuto", ma è vicino. La prossima sezione "ora locale non qualificata" è ancora più vicina, ma di nuovo non è abbastanza.
geneorama

Sì, sto fissando la canna del refactoring del mio schema proprio per questo proprio ora. Prendi una situazione di noleggio auto. Per ritirare un'auto da una società di noleggio, la società deve essere aperta; quindi specifichi una data e un'ora per il ritiro. Tuttavia, molti hanno scatole keydrop; così cadi dopo ore. Quindi se la posizione è chiusa la domenica; c'è una data di consegna; ma non un tempo. La memorizzazione di un valore 0 (es. 12:00) non funzionerà perché alcune posizioni sono aperte fino a mezzanotte, che è un valore valido in altre situazioni.
Reece,

5

Preferirò sempre archiviarlo come una singola colonna, a meno che non ci siano richieste specifiche di business / applicazioni. Di seguito sono riportati i miei punti -

  • L'estrazione di tempo dal timestamp non è un problema
  • Perché aggiungere una colonna extra solo per tempo se possiamo memorizzarli entrambi insieme
  • Per evitare di aggiungere la data e l'ora ogni volta che si esegue una query.

1
@a_horse_with_no_name ha un punto qui. Penso che "L'estrazione del timestamp dal datetimestamp non è un problema" dovrebbe essere riformulato come "L'estrazione del tempo dal timestamp non è un problema" . "Timestamp" di solito indica sia la data che l'ora (e di solito il fuso orario).
ypercubeᵀᴹ

Sì, accetta @ ypercubeᵀᴹ. Il timestamp di solito indica sia la data che l'ora. Ho esplicitamente menzionato la parola DateTimeStamp, quindi chiunque può capire che stiamo parlando di data e ora di entrambi. Ma hai anche ragione. Modificata la risposta.
Ashwini Mohan,

3

In SQL Server è consigliabile archiviare DataTime come un campo. Se si crea un indice sulla colonna DataTime, può essere utilizzato come ricerca per data e come ricerca per data. Pertanto, se è necessario limitare tutti i record esistenti per la data specifica, è comunque possibile utilizzare l'indice senza dover fare nulla di speciale. Se è necessario eseguire una query per la fascia oraria, non sarà possibile utilizzare lo stesso indice e, quindi, se si dispone di un caso aziendale in cui ci si interessa di più dell'ora del giorno rispetto a DateTime, è necessario memorizzarlo separatamente in quanto sarà necessario creare un indice su di esso e migliorare le prestazioni.


1

In effetti, è un peccato che non esista un tipo di DB-cross standard per questo (come INT e VARCHAR sono per numeri interi e valori di stringa). I 2 approcci cross-database che ho incontrato finora stanno usando le colonne VARCHAR / CHAR per archiviare i valori DataTime come stringhe formattate secondo lo standard ISO 8601 (più conveniente, leggibile dall'uomo) e usando BIGINT per memorizzarli come timestamp POSIX (più informazioni in modo efficiente, più veloce, più facile da manipolare matematicamente).


2
Sì, c'è: timestampquesto è ciò che definisce lo standard SQL. Memorizzare i timestamp come stringhe è un pessimo consiglio
a_horse_with_no_name

0

Dopo aver letto un sacco di cose, il tempo UTC Unix in BIGINT sembra essere la soluzione ottimale. ID timesone TZDB in VARCHAR per la memorizzazione del fuso orario, se necessario. Alcuni argomenti:

  1. TIMESTAMP e DATETIME eseguono un sacco di conversioni ingannevoli in background che sembrano essere complesse e non chiare. Il server passa dall'ora locale all'ora UTC o all'ora del server e viceversa, a volte o meno. Un mucchio di spese generali nascoste per ogni funzione.

  2. BIGINT (8kb) è almeno altrettanto leggero o più leggero di DECIMAL richiesto per l'archiviazione in formato xxxxxx.xxxxxx, che è praticamente memorizzato come due INT + qualcosa da MySQL . Ed è sufficiente conservare secoli avanti.

  3. Praticamente tutti i principali linguaggi di programmazione hanno librerie di funzioni standard per lavorare con Unix time.

  4. Le operazioni matematiche con BIGINT dovrebbero essere più o meno veloci di qualsiasi altra cosa su qualsiasi hardware.

Naturalmente tutto quanto sopra è rilevante per grandi progetti internazionali. Per qualcosa di piccolo, andare con il formato predefinito del framework scelto sembra essere abbastanza buono.


2
" fai un sacco di conversioni ingannevoli in background che sembrano essere ... non chiare " - di quale DBMS stai parlando? Per una timestampcolonna non si verificano "conversioni ingannevoli" (a livello di database) e per timestamp with time zonequesto è ben documentato e spiegato nei manuali (almeno per Oracle e Postgres)
a_horse_with_no_name

1
"Praticamente tutti i principali linguaggi di programmazione hanno librerie di funzioni standard per lavorare con Unix time." Eppure butti via tutte le librerie e le funzioni su date, orari e timestamp che hanno SQL / DBMS, con la tua scelta di usare bigint ...
ypercubeᵀᴹ
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.