Viele fielen 2020 einem seltsamen Phänomen der Zeitwahrnehmung zum Opfer, aber einige Datenbankverwaltungssysteme manipulieren die Zeit viel länger. Ich habe dies zum ersten Mal bemerkt, als ein Freund von mir in einem seiner Projekte ( Accord ist ein beliebter Discord-Bot) bei Verwendung mit EF Core auf die folgende Ausnahme vom MySQL-Connector stieß:
MySqlException: Incorrect TIME value: '960:00:00.000000'
Nicht zu MySQL-versiert (da ich PostgreSQL aus Gründen bevorzuge, die bald offensichtlich werden), dachte ich für eine Sekunde, dass die Anzahl der Stunden falsch war. Es ist anzunehmen, dass die TIME-Werte auf 24 Stunden begrenzt sind oder dass Werte, die sich über mehrere Tage erstrecken, eine andere Syntax erfordern - beispielsweise
40:00:00:00
40 Tage. Die Realität erwies sich jedoch als viel komplizierter und verwirrender.
Der nächste naheliegende Schritt war die Überprüfung der MySQL-Dokumentation . Es las:
MySQL empfängt und zeigt TIME-Werte im Format 'hh: mm: ss' an (oder im Format 'hhh: mm: ss' für große Stundenwerte).
Bisher ist alles in Ordnung: Unser problematischer TIME-Wert passt gut in dieses Format, obwohl die Tatsache, dass
hh
sie hhh
explizit angegeben werden, Verdacht erregt (was ist mit Taktwerten über 999?). Der folgende Satz in der Dokumentation erklärt teilweise alles und regt dabei eine Reihe von Fragen wie "Was zum ...?" An:
Die TIME-Werte können zwischen '-838: 59: 59' und '838: 59: 59' liegen.
Na gut ... Irgendeine seltsame Reichweite. Dafür muss es einen guten technischen Grund geben. 839 Stunden sind 34.958 (3) Tage, und der gesamte Bereich beträgt genau 6040798 Sekunden. Die Dokumentation lautet wie folgt:
MySQL erkennt TIME-Werte in verschiedenen Formaten, von denen einige Sekundenbruchteile mit bis zu 6 Dezimalstellen (Mikrosekunden) enthalten können.
Mit anderen Worten beträgt das gesamte Intervall 6.040.798.000.000 Mikrosekunden. Wieder eine seltsame Zahl. Es ist weit entfernt von einer Zweierpotenz (zwischen 2 42 und 2 43 ), daher scheint MySQL ein einzigartiges internes Darstellungsformat zu verwenden. Bevor ich jedoch auf dieses Problem eingehe, möchte ich darauf hinweisen, wie schlecht dieser Typ ist.
Dies ist alles, was MySQL zur Messung von Zeitintervallen zu bieten hat, wobei die gesamte Zeitspanne etwas mehr als einen Monat beträgt. Wie groß ist dieses "kleine bisschen"? Wie Sie sehen können, ist es nicht einmal ein Vielfaches einer ganzzahligen Anzahl von Tagen.
Schlimmer noch, der beliebteste MySQL-zu- EF-Core- Anbieter konvertiert .NET standardmäßig
TimeSpan
in TIME, obwohl dies der Fall istTimeSpan
kann Intervalle von mehreren zehn Jahrtausenden enthalten (es werden 64-Bit-Ganzzahlen verwendet, und die akzeptable Genauigkeit beträgt 10 bis 8 s). Vergleichen Sie dies mit ein paar Monaten in TIME. Andere Personen sind auf
dieses Problem gestoßen , und die Diskussion in der entsprechenden Ausgabe enthält einen Verweis auf das Verhalten von SQL Server: "Dies ahmt das Verhalten von SQL Server nach". Ich habe überprüft - tatsächlich hat der SQL Server- Zeittyp einen Bereich von 00: 00: 00.0000000 bis 23: 59: 59.9999999, was im Allgemeinen viel vernünftiger ist als der seltsame Zeitbereich.
Aber kommen wir zurück zu MySQL. Was ist der Grund für solch ein ungewöhnliches Sortiment? Im MySQL-Gerätehandbuchsagt, dass sich in Version 5.6.4 der TIME-Typ geändert hat und Sekundenbruchteile unterstützt werden. Für den gesamten Teil werden drei Bytes verwendet. Wenn diese drei Bytes vollständig zum Codieren von Sekunden verwendet werden, ergibt sich eine Zeitspanne von mehr als 2.330 Stunden - viel mehr als das derzeitige Maximum von 838 Stunden (obwohl selbst dies beim Konvertieren von
TimeSpan
'a nicht sehr nützlich ist ).
Dies bedeutet, dass der Prozess, der die Zeit in MySQL codiert, Bits verschwendet - möglicherweise aus Gründen der Benutzerfreundlichkeit (obwohl ich nicht sicher bin, unter welchen Umständen dies relevant ist). Vielleicht ist dies sinnvoll, wenn das DBMS (und die Vorstellung der Entwickler, was Benutzer damit machen werden) auf die Arbeit mit Zeichenfolgen ausgerichtet ist und die Entwickler die Präsentation beschleunigen möchten
hh:mm:ss
.
Also siehe:
1 — (1 = , 0 = )
1 ( )
10 — (0-838)
6 — (0-59)
6 — (0-59)
— 24 = 3
Das erklärt alles, nicht wahr? Schauen wir uns das genauer an. 10 Bits für Stunden ... und der Bereich reicht von Null bis 838. Ich möchte Sie schnell daran erinnern, dass 2 10 = 1024, nicht 838. Die Intrige gewinnt an Dynamik ...
Natürlich bin ich nicht die erste Person, die diese Frage gestellt hat (ich habe dies bereits bei StackOverflow gefragt ). Alles scheint in der "akzeptierten" Antwort dort angegeben zu sein, aber die seltsame Wahl von 838 Stunden wird zuerst durch "Abwärtskompatibilität mit Anwendungen, die vor langer Zeit geschrieben wurden" erklärt, und erst dann wird erwähnt, dass dies übrigens etwas mit Kompatibilität mit MySQL 3 zu tun hat Windows 98 galt damals als Neuheit, und Linux war noch nicht einmal 10 Jahre alt.
In MySQL 3 verwendete der TIME-Typ auch 3 Bytes, nur auf eine völlig andere Weise. Eines der Bits war ebenfalls für das Vorzeichen reserviert, aber die verbleibenden 23 Bits entsprachen ganzen Zahlen, die wie folgt erhalten wurden: Stunden × 10.000 + Minuten × 100 + Sekunden. Mit anderen Worten, die zwei am wenigsten signifikanten Ziffern waren Sekunden, die nächsten zwei waren Minuten und die verbleibenden zwei waren Stunden. 2 * 23 ist 83888608, also 838: 86: 08, daher beträgt der maximal gültige Zeitwert in diesem Format 838: 59: 59.
Dieses Format ist noch weniger praktisch als das aktuelle, da es für fast jede Zeitoperation eine Multiplikation und Division erfordert (mit Ausnahme der Formatierung und Analyse von Zeichenfolgen - was wiederum beweist, dass MySQL der Zeichenfolgen-E / A zu viel Aufmerksamkeit schenkt und sich nicht wirklich um das Vorhandensein von Typen kümmert. Dies wäre praktisch für interne Operationen und nicht auf Zeichenfolgen basierende Protokolle.
Die MySQL-Entwickler konnten diesen Typ viele Male beheben oder zumindest eine Alternative bereitstellen, die frei von den bestehenden Einschränkungen ist. Der TIME-Typ hat sich seit MySQL 3 bis heute zweimal geändert, aber jedes Mal ist der seltsame Bereich gleich geblieben - möglicherweise aus Kompatibilitätsgründen.
Ich kann mir keine Situation vorstellen, in der das Erweitern des Wertebereichs für einen Typ die Anwendungskompatibilität beeinträchtigen könnte: Haben Typen in MySQL ein spezifisches Überlaufverhalten? Welcher vernünftige Programmierer würde sich auf interne Datenbanktypbeschränkungen verlassen, um irgendetwas in seiner Anwendung zu validieren? Wenn es eine solche Person gibt, warum um alles in der Welt würde er sich plötzlich entscheiden, diese lächerliche Grenze von 838 Stunden ohne Änderungen in das Datenmodell seiner Anwendung zu übertragen? Um ehrlich zu sein, möchte ich nicht einmal die Antworten auf diese Fragen wissen.
Trotz einiger wichtiger Veränderungen in der Geschichte von MySQL ist der TIME-Typ immer noch umständlich und begrenzt. Und der Höhepunkt des Programms hier ist meiner Meinung nach das unbenutzte Bit "reserviert für zukünftige Erweiterungen". Ich hoffe, dass es auf lange Sicht auf den alten, alten TIME-Wert verweist und bis dahin MySQL und / oder MariaDB einen vernünftigen zeitlichen Typ wie INTERVAL in PostgreSQL haben , der einen Bereich von ± 178.000.000 Jahren und eine Mikrosekunde hat Richtigkeit.
PS vom Übersetzer
Lesen Sie auch in unserem Blog: