Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Datenbanken (https://www.delphipraxis.net/15-datenbanken/)
-   -   Delphi MSSQL - DateTime to Int liefert unterschiedliche Werte (https://www.delphipraxis.net/206337-mssql-datetime-int-liefert-unterschiedliche-werte.html)

Aviator 11. Dez 2020 12:06

Datenbank: MSSQL • Version: 2016 • Zugriff über: FireDAC

MSSQL - DateTime to Int liefert unterschiedliche Werte
 
Hallo zusammen,

ich habe hier ein doch recht kurioses Problem mit MSSQL und Delphi.

Ich rufe Datensätze aus der Datenbank ab, die nach einem Datum eingeschränkt werden sollen. In diese speziellen Beispiel wird abgefragt, ob das Belegdatum größer oder gleich dem übergebenen Datum ist. Anfänglich ist mir das gar nicht aufgefallen, doch jetzt stelle ich fest, dass eine Abfrage ob das Datum >= 30.11.2020 ist keine Werte zurückliefert obwohl das eigentliche Belegdatum der 29.11.2020 ist.

Das Statement wird mit Parametern an den Server übergeben. Also als Beispiel mit
Delphi-Quellcode:
Query.Parameters('DateParam').Value := StrToDate('30.11.2020');
. Der echte Wert den ich hier übergebe kommt aus einer anderen Datenbank.

Ich habe nun mit dem SQL Profiler die Query abgefangen die an den Server gesendet wird. Wenn ich diese manuell auf dem Server ausführe kommt ebenfalls kein Ergebnis. Setze ich den Integer Wert in den das Datum bei der Übergabe umgewandelt wird um einen Tag zurück (also Integer - 1), dann wird der Datensatz angezeigt.

Baue ich mir jetzt auf dem SQL Server eine Query zusammen die mir aus einem Datum einen Integer und aus einem Integer ein Datum macht, dann unterscheiden sich die Werte von dem, was Delphi an die Datenbank übergibt. Eine Prüfung auf einer Website liefert allerdings das gleiche Ergebnis wie das von Delphi.


Der Wert 43831 liefert in Delphi als Datum 01.01.2020. Die folgende SQL Abfrage liefert auf dem Server allerdings als Datum dem 03.01.2020.
SQL-Code:
DECLARE @d1 int = 43831
SELECT CONVERT(datetime, @d1, 104)
Umgekehrt herum zeigt der SQL Server beim Konvertieren des Datums in einen Integer Wert den gleichen Wert an, in das er auch dieses Datum umwandelt. Also in sich ist der SQL Server korrekt. Nur die Werte passen nicht zu denen von Delphi und der Website.

Diese Abfrage liefert als Ergebnis 43831. Bei Delphi ist dieser Wert aber bereits der 01.01.2020.
SQL-Code:
DECLARE @d1 datetime = '03.01.2020 00:00:00'
SELECT CONVERT(INT, CONVERT(DATETIME, @d1))

Das müsste doch schon irgendjemandem bereits aufgefallen sein. Es sind also immer zwei Tage Differenz zwischen Delphi und MSSQL.

Hat das jemand eine Idee was hier schief gehen könnte.

Delphi.Narium 11. Dez 2020 12:39

AW: MSSQL - DateTime to Int liefert unterschiedliche Werte
 
Siehe Doku:
https://docs.microsoft.com/de-de/sql...l-server-ver15
http://docs.embarcadero.com/products...TDateTime.html

MSSQL beginnt beim 1.1.1900
Delphi beginnt beim 30.12.1899

Das ergibt eine Differenz von exakt 2 Tagen.

Uwe Raabe 11. Dez 2020 12:43

AW: MSSQL - DateTime to Int liefert unterschiedliche Werte
 
Zitat:

Zitat von Aviator (Beitrag 1478941)
dass eine Abfrage ob das Datum >= 30.11.2020 ist keine Werte zurückliefert obwohl das eigentliche Belegdatum der 29.11.2020 ist.

Das sieht mir aber vollkommen korrekt aus. Der 29.11.2020 ist ja auch nicht größer oder gleich dem 30.11.2020 :gruebel:

Ist da vielleicht noch irgendwo ein Tippfehler?

Aviator 11. Dez 2020 12:47

AW: MSSQL - DateTime to Int liefert unterschiedliche Werte
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1478948)
Zitat:

Zitat von Aviator (Beitrag 1478941)
dass eine Abfrage ob das Datum >= 30.11.2020 ist keine Werte zurückliefert obwohl das eigentliche Belegdatum der 29.11.2020 ist.

Ist da vielleicht noch irgendwo ein Tippfehler?

Ja sorry. Genau anders herum. Es gibt ein Belegdatum und ein Startdatum ab wann die Prüfung laufen soll. Das Belegdatum muss >= dem Startdatum sein, damit es in die Prüfung mit einfließt.


Zitat:

Zitat von Delphi.Narium (Beitrag 1478947)
Siehe Doku:
https://docs.microsoft.com/de-de/sql...l-server-ver15
http://docs.embarcadero.com/products...TDateTime.html

MSSQL beginnt beim 1.1.1900
Delphi beginnt beim 30.12.1899

Das ergibt eine Differenz von exakt 2 Tagen.

Naja da würde ich mal sagen, dass das ein ganz klarer Fall von PEBKAC ist. Danke für die Info. Das war mir jetzt neu. Da muss ich mal prüfen, wo das evtl. überall Probleme bereiten könnte.

Die Frage wäre jetzt allerdings, ob es hierfür eine einfache Lösung gibt, ohne das ich jedes Mal daran denken muss die zwei Tage abzuziehen.

Delphi.Narium 11. Dez 2020 12:55

AW: MSSQL - DateTime to Int liefert unterschiedliche Werte
 
Ja, die Lösung ist ganz einfach:

Datum als Datum speichern und nicht als Integer.

Oder:
Delphi-Quellcode:
Query.Parameters('DateParam').AsDateTime := StrToDate('30.11.2020');

Delphi-Quellcode:
Query.Parameters('DateParam').AsString := '30.11.2020';

Aviator 11. Dez 2020 13:15

AW: MSSQL - DateTime to Int liefert unterschiedliche Werte
 
Hmm ... Wald und Bäume und so. Schaue ich mir direkt mal an ob es dann problemlos funktioniert.

Bernhard Geyer 11. Dez 2020 14:31

AW: MSSQL - DateTime to Int liefert unterschiedliche Werte
 
Zitat:

Zitat von Delphi.Narium (Beitrag 1478950)
Ja, die Lösung ist ganz einfach:

Datum als Datum speichern und nicht als Integer.

Oder notfalls als String im ISO-Format (Haben wir, da vor 20 Jahren bei Einführung jedes DBMS noch wild eigenes Verhalten bei Datumswerten zeigte)

Aviator 11. Dez 2020 17:58

AW: MSSQL - DateTime to Int liefert unterschiedliche Werte
 
Ok. Der Test hat etwas länger gedauert. Ein simples Benutzen von Parameter().AsDate hat einen Fehler geworfen.
Delphi-Quellcode:
[FireDAC][Phys][ODBC][Microsoft][SQL Native Client]Optionales Feature wurde nicht implementiert
. Ich habe jetzt einfach mal noch versucht auf den neuen ODBC Treiber umzustellen anstatt den Native Client zu benutzen. Da gab es dann noch ein paar Schwierigkeiten mit dem ConnectionString. Jetzt scheint es aber zu laufen und den Datensatz hat er auch gefunden.

Danke für die Hilfe. Jetzt weiß ich zumindest mal woher der Fehler kam. :thumb:

generic 11. Dez 2020 21:47

AW: MSSQL - DateTime to Int liefert unterschiedliche Werte
 
Kleiner Tip noch am Rande:
Ich sehe hier viele Funktionen, welche von den Ländereinstellungen abhängig sein.
z.B. StrToDate von den OS und die Convert-Funktionen von der Sprache im Server bzw. der Anmeldung.

Versucht hier am besten unabhängig zu werden von den L.Einstellungen.

Eine Lösung wurde bereits genannt, z.B. nicht zu konvertieren und überall die richtigen Datentypen zu verwenden.

Aviator 11. Dez 2020 22:44

AW: MSSQL - DateTime to Int liefert unterschiedliche Werte
 
Zitat:

Zitat von generic (Beitrag 1478986)
Eine Lösung wurde bereits genannt, z.B. nicht zu konvertieren und überall die richtigen Datentypen zu verwenden.

Da werde ich zukünftig auch genauer darauf achten anstatt immer nur
Delphi-Quellcode:
.Value
zu nehmen. Der
Delphi-Quellcode:
StrToDate()
Aufruf war nur ein Beispiel um hier etwas zu demonstrieren. In Wirklichkeit wurde, wie ich im ersten Beitrag geschrieben hatte, ein TDate Wert aus einem anderen Objekt an den Parameter übergeben.

Trotzdem danke für den Hinweis. :)

Delphi.Narium 12. Dez 2020 10:32

AW: MSSQL - DateTime to Int liefert unterschiedliche Werte
 
Ein Datum ist bei mir immer .AsDateTime, egal welche Datenbank. Und wenn ich keine Uhrzeit habe? Ja und, dann ist es halt .AsDateTime um 0:00 Uhr.

Damit hatte ich noch nie irgendwelche Probleme. Mir war bis gestern nicht mal bekannt, dass es bei Delphi und MSSQL überhaupt die technische Möglichkeit gibt, einen "Zweitagesfehler" zu provozieren.

Bis jetzt (ca. 20 Jahre) musste ich mich nie um irgendeine Datumskonvertierung zwischen Delphi und einer beliebigen Datenbank kümmern. Die Schnittstellen haben das bisher immer transparent gemanagt. Damit umgehe ich auch eventuell mögliche Probleme, die durch unterschiedliche Ländereinstellungen bei Client / Server / Datenbank ggfls. für Probleme sorgen könnten. Die Datenbankschnittstellen sind eigentlich alle intelligent genug implementiert, um damit umgehen zu können.

.Value ist meiner Meinung nach die schlechtmöglichste Alternative: Das ist ein Variant. Und bei Varianten wird immer interpretiert, "was wohl am ehesten gemeint sein könnte". .Value ist für Typsicherheit eher ungeeignet.

Wenn ich 'nen String habe, nehme ich .AsString,
bei 'nem Integer .AsInteger,
bei 'nem Gleitkommawert .AsFloat,
bei 'nem Boolean .AsBoolean.

Warum nimmt man in Gottesnamen bei 'nem Date, Time, DateTime nicht .AsDateTime, sondern 'nen Integer? Nur weil man ein Datum ohne Uhrzeitanteil zufällig auch als Integer darstellen kann? (Bei Linux sind es übrigens Sekunden seit 1.1.1970, da wäre die Differenz irgendwie anders und wohl meist ein bisserl größer und garantiert nicht immer konstant.)

Bei 'nem String, der zufällig nur Ziffern (ohne Nachkommaanteil) enthält, nimmt doch auch nicht .AsInteger, nur weil das zufällig auch meistens klappt.

Ein String ist ein String.
Ein Integer ist ein Integer.
Ein Datum ist ein Datum.

.Value nehme ich nur, wenn ich nicht weiß was es ist und mir auch egal ist, was es ist. Hauptsache irgendwie rein in die Datenbank und ggfls. auch wieder irgendwie raus. Die, die dieses "Irgendwas" nutzen wollen, müssen sich dann selbst drum kümmern, dass sie damit auch was anfangen können. (Kommt in (meinem) täglichen Leben eher extrem selten bis garnicht vor ;-))

Was auch gut geht: Hab' ich ein Datum nur als Zeichenfolge, dann nehme ich .AsString und konvertiere nicht selber. Die Datenbanksschnittstellen kriegen das gehändelt und jede Datenbank bekommt damit das für sie richtige Datumsformat. Und wenn das nicht klappt (weil die Zeichenfolge einen Wert enthält, der nicht in ein gültiges Datum verwandelt werden kann), dann fliegt 'ne Exception. Darauf kann man dann reagieren. Aber man muss garantiert nicht nach 'ner Zeitdifferenz suchen, die nur dadurch entstehen kann, dass man absolut ungeeignete Werte verwendet, die technisch zufällig von beiden Seiten "irgendwie" verstanden werden können.

Aviator 12. Dez 2020 10:47

AW: MSSQL - DateTime to Int liefert unterschiedliche Werte
 
Zitat:

Zitat von Delphi.Narium (Beitrag 1478995)
Warum nimmt man in Gottesnamen bei 'nem Date, Time, DateTime nicht .AsDateTime, sondern 'nen Integer? Nur weil man ein Datum ohne Uhrzeitanteil zufällig auch als Integer darstellen kann? (Bei Linux sind es übrigens Sekunden seit 1.1.1970, da wäre die Differenz irgendwie anders und wohl meist ein bisserl größer und garantiert nicht immer konstant.)

Das Thema ist doch schon längst abgehakt. Kein Grund noch irgendwie ausfallend zu werden. Die Übergabe mit .Value war ein Fehler, den ich so nicht mehr machen werde. Die Umwandlung in einen Integer kam durch die Implementierung in Delphi. Und dadurch kam dann auch die Differenz zustande.

Und das es in Linux Sekunden ab 01.01.1970 sind ist mir auch bekannt. Nur war mir eben nicht bekannt, dass der Delphi TDate Datentyp einen anderen Startpunkt hat als der von MSSQL.

Delphi.Narium 12. Dez 2020 11:02

AW: MSSQL - DateTime to Int liefert unterschiedliche Werte
 
Sorry, es tut mir leid.

Ich wollte Dir nicht zu nahe treten, es war auch nicht ausfallend gemeint.

Du bist hier halt nur über ein Problem gestolpert, dass mich quasi schon seit Jahren verfolgt, da Probleme mit Datumswerten immer wieder auftreten. Du bist damit also nicht alleine.

Mir ging es eigentlich nur darum, auf die bestehende Grundproblemetik bei Datumswerten irgendwie hinzuweisen, absolut losgelöst von Deiner Person.

Nur damit andere, die später auf diesen Thread stoßen, Analogien für ihre eigene Fragestellung finden können.

Offensichtlich ist mir das aber deutlich misslungen.

Tut mir leid, das war nicht meine Absicht.

Aviator 12. Dez 2020 11:18

AW: MSSQL - DateTime to Int liefert unterschiedliche Werte
 
Zitat:

Zitat von Delphi.Narium (Beitrag 1478998)
Offensichtlich ist mir das aber deutlich misslungen.

Eigentlich war es nur der von mir zitierte Satz der etwas "überheblich/ausfallend" rüberkam. Ich bin dir auch nicht böse deshalb. Für die Nachwelt sind sicherlich noch einige interessante Informationen dabei.


Also alles cool. :cheers:

Redeemer 13. Dez 2020 00:38

AW: MSSQL - DateTime to Int liefert unterschiedliche Werte
 
In OLE beginnt die Epoche am 0. Januar des Schaltjahres 1900. Ja, es ist ausdrücklich nicht der 31. Dezember 1899 (formatiert mal in Excel 0 oder 60 als Datum). Und ja, 1900 war kein Schaltjahr.
Aus den Gründen hat Delphi den minus-1. Januar des Normaljahres 1900 (auch 30. Dezember 1899 genannt) als Start der Epoche. Bei Tagen ab dem 1.3.1900 kann man bei der Arbeit mit OLE den Unterschied ignorieren. OLE kennt keine Datumsangaben vor Beginn der Epoche.
Die Epoche beginnt bei SQL Server am 1. Januar des Normaljahres 1900. Warum Microsoft da ein anderes Datum als bei ihren anderen Produkten gewählt hat, erschließt sich mir nicht.

Da muss man höllisch bei überladenen Methoden aufpassen, die was an die Datenbank übergeben, da man in Delphi Double und TDateTime nicht unterscheiden kann, sonst hat man die zwei Tage Differenz.


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:07 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz