Bevorzugte Methode zum Speichern von DateTime

18

Wir können Datums- und Zeitinformationen auf verschiedene Arten speichern. Was ist die beste Methode zum Speichern von DateTime-Informationen?

Speichern von Datum und Uhrzeit in 2 separaten Spalten oder einer Spalte mit DateTime ?

Können Sie erklären, warum dieser Ansatz besser ist?

(Link zu MySQL-Dokumenten als Referenz. Die Frage ist allgemein und nicht spezifisch für MySQL.)
Datums- und Zeittypen: Datum und Zeit

julianisch
quelle
3
Das hängt weitgehend davon ab, welches Datenbanksystem Sie verwenden. Was es wert ist: Oracle hat sich dafür entschieden, dies als eine Spalte (als DATETIME-Datentyp) zu tun. In diesem Fall ist die Verwendung der integrierten Unterstützung sicherlich besser als das Speichern dieser Informationen in zwei Spalten als NUMBER-Datentypen (auch wenn Sie dies nur tun) brauche 1 Teil für eine gegebene Abfrage ... das Datum oder die Uhrzeit).
Kris Johnston
5
Für SQL Server ist ein Fall, in dem eine Aufteilung bevorzugt werden kann, die Gruppierung nach Datum. Ein Stream-Aggregat kann ohne Sortierung für den zusammengesetzten Index date,time mit, group by dateaber nicht für einen Index datetime mit verwendet werden group by cast(datetime as date), obwohl es die gewünschte Reihenfolge liefern würde.
Martin Smith
1
Beachten Sie, dass für die Berechnung von Zeitwerten die Kenntnis von Datum und Zeitzone erforderlich ist - z. B. hängt die Entfernung zwischen zwei Zeiten davon ab, ob dieser Tag ein DST-Ereignis enthält, einige Tage 23 oder 25 Stunden haben und auch Schaltsekunden vorhanden sind.
Peteris

Antworten:

23

Das Speichern der Daten in einer einzelnen Spalte ist die bevorzugte Methode, da sie untrennbar miteinander verbunden sind. Ein Zeitpunkt ist eine einzelne Information, nicht zwei.

Eine übliche Methode zum Speichern von Datums- / Zeitdaten, die von vielen Produkten "hinter den Kulissen" verwendet werden, besteht darin, sie in einen Dezimalwert umzuwandeln, wobei "Datum" der ganzzahlige Teil des Dezimalwerts und "Zeit" der Bruchteil ist Wert. So wird zwischen 1900-01-01 00:00:00 als 0,0 und dem 20. September 2016 zwischen 9:34:00 als 42631.39861 gespeichert. 42631 ist die Anzahl der Tage seit dem 01.01.1900. .39861 ist die Zeit, die seit Mitternacht vergangen ist. Verwenden Sie dazu nicht direkt einen Dezimaltyp, sondern einen expliziten Datums- / Zeittyp. Mein Punkt hier ist nur eine Illustration.

Wenn Sie die Daten in zwei separaten Spalten speichern, müssen Sie beide Spaltenwerte immer dann kombinieren, wenn Sie sehen möchten, ob ein bestimmter Zeitpunkt früher oder später als der gespeicherte Wert liegt.

Wenn Sie die Werte separat speichern, werden Sie ausnahmslos auf "Bugs" stoßen, die schwer zu erkennen sind. Nehmen wir zum Beispiel folgendes:

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

Im obigen Code erstellen wir eine Testtabelle, füllen sie mit zwei Werten und führen dann eine einfache Abfrage für diese Daten durch. Die erste SELECTgibt beide Zeilen zurück, die zweite SELECTgibt jedoch nur eine einzelne Zeile zurück, was möglicherweise nicht das gewünschte Ergebnis ist:

Bildbeschreibung hier eingeben

Die richtige Methode zum Filtern eines Datums- / Zeitbereichs, in dem sich die Werte in separaten Spalten befinden, wie von @ypercube in Kommentaren hervorgehoben, lautet:

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

Wenn Sie die Zeitkomponente zu Analysezwecken getrennt benötigen , können Sie eine berechnete, dauerhafte Spalte für den Zeitanteil des Werts hinzufügen:

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

SELECT *
FROM #dt;

Bildbeschreibung hier eingeben

Die persistierte Spalte könnte dann indiziert werden, um schnelle Sortierungen usw. nach Tageszeit zu ermöglichen.

Wenn Sie das Datum und die Uhrzeit für Anzeigezwecke in zwei Felder aufteilen möchten, sollten Sie beachten, dass die Formatierung auf dem Client und nicht auf dem Server erfolgen sollte.

Max Vernon
quelle
11

Ich werde eine abweichende Meinung zu den anderen Antworten abgeben.

Wenn sowohl die Datums- als auch die Zeitkomponente zusammen benötigt werden, dh ein Eintrag ungültig ist, wenn er einen, aber keinen anderen enthält (oder in einem, aber keinem anderen, NULL ist), ist das Speichern in einer einzelnen Spalte aus den in anderen angegebenen Gründen sinnvoll Antworten.

Es kann jedoch vorkommen, dass eine oder beide Komponenten einzeln optional sind. In diesem Fall wäre es falsch, es in einer einzelnen Spalte zu speichern. Wenn Sie dies tun, müssen Sie NULL-Werte auf eine beliebige Weise darstellen, z. B. indem Sie die Zeit als 00:00:00 speichern.

Hier einige Beispiele:

  • Sie erfassen Fahrzeugfahrten für Meilensteuerabzüge. Die genaue Uhrzeit der Reise zu kennen, wäre hilfreich. Wenn ein Mitarbeiter sie jedoch nicht notiert und vergessen hat, sollte das Datum dennoch selbst aufgezeichnet werden (erforderliches Datum, optionale Uhrzeit).

  • Sie führen eine Umfrage durch, um herauszufinden, wann die Leute zu Mittag essen, und Sie bitten die Teilnehmer, ein Formular mit einer Stichprobe ihrer Mittagspausen, einschließlich Daten, auszufüllen. Einige machen sich nicht die Mühe, das Datum einzugeben, und Sie möchten die Daten nicht verwerfen, da dies die Zeit ist, die Ihnen wirklich am Herzen liegt (optionales Datum, erforderliche Zeit).

Siehe diese verwandte Frage für alternative Ansätze.

JBentley
quelle
In RFC 3339 gibt es eine Konvention zum Aufzeichnen von "unknown local offset". Ich denke, es deckt den Anwendungsfall "unbekannte Zeit" nicht ganz ab, aber es ist nah. Der nächste Abschnitt "Unqualifizierte Ortszeit" ist noch näher, aber es ist nicht ganz genug.
Genorama
Ja, ich starre aus diesem Grund gerade auf die Überarbeitung meines Schemas. Nehmen Sie eine Autovermietungssituation. Um ein Auto bei einem Vermieter abzuholen, muss das Unternehmen geöffnet sein. Sie geben also ein Datum und eine Uhrzeit für die Abholung an. Viele haben jedoch Keydrop-Boxen; Sie steigen also nach Stunden aus. Also, wenn der Standort sonntags geschlossen ist; es gibt ein Abgabedatum; aber keine Zeit. Das Speichern eines 0-Werts (z. B. 12 Uhr morgens) funktioniert nicht, da einige Standorte bis Mitternacht geöffnet sind, was in anderen Situationen ein gültiger Wert ist.
Reece
5

Ich bevorzuge es immer, das als eine einzelne Spalte zu speichern, es sei denn, es gibt eine bestimmte Geschäfts / Anwendungsnachfrage. Unten sind meine Punkte -

  • Das Extrahieren der Zeit aus dem Zeitstempel ist kein Problem
  • Warum sollte man nur für kurze Zeit eine zusätzliche Spalte hinzufügen, wenn wir beide zusammen speichern können?
  • Um zu vermeiden, dass bei jeder Abfrage Datum und Uhrzeit hinzugefügt werden.
Ashwini Mohan
quelle
1
@a_horse_with_no_name hat hier einen Punkt. Ich denke, das "Extrahieren des Zeitstempels aus dem Datumsstempel ist kein Problem" sollte umformuliert werden als "Extrahieren der Zeit aus dem Zeitstempel ist kein Problem" . "Zeitstempel" bedeutet normalerweise sowohl Datum als auch Uhrzeit (und normalerweise Zeitzone).
Ypercubeᵀᴹ
Ja, stimme @ ypercubeᵀᴹ zu. Zeitstempel bedeutet normalerweise sowohl Datum als auch Uhrzeit. Ich habe das Wort DateTimeStamp ausdrücklich erwähnt, damit jeder verstehen kann, dass es sich um Datum und Uhrzeit handelt. Sie haben aber auch Recht. Antwort geändert.
Ashwini Mohan
3

In SQL Server ist es am besten, DataTime als ein Feld zu speichern. Wenn Sie einen Index für die DataTime-Spalte erstellen, kann dieser als Datumssuche und als DateTime-Suche verwendet werden. Wenn Sie also alle Datensätze einschränken müssen, die für das bestimmte Datum existieren, können Sie den Index trotzdem verwenden, ohne etwas Besonderes tun zu müssen. Wenn Sie einen Zeitabschnitt abfragen müssen, können Sie nicht denselben Index verwenden. Wenn Sie also einen Geschäftsfall haben, bei dem die Uhrzeit wichtiger ist als DateTime, sollten Sie ihn separat speichern, da Sie ihn erstellen müssen einen Index dafür und verbessern die Leistung.

Vladimir Oselsky
quelle
1

Tatsächlich ist es schade, dass es hierfür keinen standardmäßigen Cross-DBMS-Typ gibt (wie INT und VARCHAR für Ganzzahlen und Zeichenfolgenwerte). Die beiden datenbankübergreifenden Ansätze, die ich bisher kennengelernt habe, verwenden VARCHAR / CHAR-Spalten, um DataTime-Werte als Zeichenfolgen zu speichern, die gemäß dem ISO 8601-Standard (praktischer, lesbarer) formatiert sind, und verwenden BIGINT, um sie als POSIX-Zeitstempel zu speichern (mehr gespeichert) effizient, schneller, einfacher mathematisch zu manipulieren).

Ivan
quelle
2
Ja timestamp, das definiert der SQL-Standard. Das Speichern von Zeitstempeln als Zeichenfolgen ist ein sehr schlechter Rat
a_horse_with_no_name
0

Nach dem Lesen einiger Dinge scheint die UTC-Unix-Zeit in BIGINT die optimale Lösung zu sein. TZDB- Zeitzonen-ID in VARCHAR zur Speicherung der Zeitzone, falls erforderlich. Ein paar Argumente:

  1. TIMESTAMP und DATETIME führen im Hintergrund einige spielerische Konvertierungen durch, die komplex und unklar erscheinen. Der Server wechselt von der Ortszeit zu UTC oder zur Serverzeit und manchmal zurück oder nicht. Ein Haufen versteckter Unkosten für jede Funktion.

  2. BIGINT (8kb) ist mindestens so leicht oder leichter als DECIMAL, das für den xxxxxx.xxxxxx-Formatspeicher erforderlich ist, der von MySQL praktisch als zwei INTs + irgendetwas gespeichert wird . Und es reicht, um Jahrhunderte vorher zu lagern.

  3. Nahezu alle wichtigen Programmiersprachen verfügen über Bibliotheken mit Standardfunktionen für die Arbeit mit Unix-Zeit.

  4. Mathematische Operationen mit BIGINT sollten auf jeder Hardware so schnell oder so schnell wie nichts anderes sein.

Natürlich ist all das für große, internationale Projekte relevant. Für etwas Kleines scheint es gut genug zu sein, mit dem Standardformat des gewählten Frameworks zu arbeiten.

Arthur Tarasov
quelle
2
" Machen Sie ein paar lustige Konvertierungen im Hintergrund, die scheinen ... nicht klar zu sein " - von welchem ​​DBMS sprechen Sie? Für eine timestampSpalte finden keine "Gimmicky Conversions" statt (auf Datenbankebene) und timestamp with time zonedies ist gut dokumentiert und in den Handbüchern erklärt (zumindest für Oracle und Postgres)
a_horse_with_no_name 25.02.18
1
"Nahezu alle wichtigen Programmiersprachen verfügen über Bibliotheken mit Standardfunktionen für die Arbeit mit Unix-Zeit." Und doch werfen Sie alle Bibliotheken und Funktionen über Datum, Uhrzeit und Zeitstempel, die SQL / DBMS hat, mit Ihrer Wahl der Verwendung von bigint ...
ypercubeᵀᴹ