CRecordset::GetFieldValue mit SQL_C_TIME



  • Hi,

    ich habe eine MySQL Tabelle mit einer Spalte die als TIME definiert ist.
    WICHTIG: Diese Spalte enthält auch Werte > 24h. Also z. B. "26:30:00".
    Jetzt ist es mir nicht möglich gewesen mittels "RFX_DATE" Werte größer als 24 h auszulesen,
    denn im DBVariant.m_pdate->year/month/day steht völlig schwachsinnigerweise das aktuelle Datum drin.
    Wer denkt sich so einen Misst aus...

    Aber wenn ich die Werte nun anstatt als "SQL_C_TIMESTRUCT" als "SQL_C_WCHAR" auslese,
    bekomme ich die Time korrekt als String mit > 24h.
    Aber das kann ja nicht effizient sein zumal ich dann erstmal ein String draus machen muss
    um wieder ein Zeitstempelobjekt zu parsen. Den String kann ich dann wegwerfen.

    Testweise habe ich auch mal versucht das ganze als SQL_C_LONG oder SQL_C_DOUBLE auszulesen.
    Dann bekommt man aber nur die Stunde zurück.

    Weis jemand wie man performant an den internen Wert (die Ticks) kommt?
    Ich gehe mal stark davon aus, dass MySQL die TIME nicht als Chars abspeichert.
    Ansonsten wären sicherlich keine performanten Zeitvergleiche möglich.
    Das wird also intern sicherlich ein "Integer" sein.
    Wie komme ich an diesen internen Integer um mir daraus einen Zeitstempel zu bauen?



  • Ein Mysql Time Wert muss als "Timespan" eingelesen werden, damit Werte > 24h richtig geparst werden.

    Ein Date Datentyp will das ding als Date + Time parsen.

    Keine Ahnung ob MFC einen "Timespan" Datentyp anbietet

    @Enumerator sagte in CRecordset::GetFieldValue mit SQL_C_TIME:

    Jetzt ist es mir nicht möglich gewesen mittels "RFX_DATE" Werte größer als 24 h auszulesen,
    denn im DBVariant.m_pdate->year/month/day steht völlig schwachsinnigerweise das aktuelle Datum drin.

    Öhm wenn der Wert für Stunden > 24 ist dann kann man so was einfach korrigieren, in dem man den datumspart ignoriert oder nachträglich auf 0 setzt
    Aber ein Date(Datum + Uhrzeit) Datentyp für einen Wert zu nutzen, welche einen "Timespan" darstellt, ist schon komisch



  • Unter MySql: Date and Time Data Type Representation kannst du das interne Format nachlesen.
    Bei TIME also mit 3 Bytes - und wenn ich das richtig nachgerechnet habe sind damit max. 194 Stunden möglich.
    Aber was soll das dann für ein Zeitwert mit >24h sein, wäre TIMESTAMP dann nicht sinnvoller?



  • @Th69 sagte in CRecordset::GetFieldValue mit SQL_C_TIME:

    Unter MySql: Date and Time Data Type Representation kannst du das interne Format nachlesen.
    Bei TIME also mit 3 Bytes - und wenn ich das richtig nachgerechnet habe sind damit max. 194 Stunden möglich.
    Aber was soll das dann für ein Zeitwert mit >24h sein, wäre TIMESTAMP dann nicht sinnvoller?

    Die 3 Bytes gelten nur für Versionen vor Mysql 5.4.6. Danach sind es
    "3 bytes + fractional-seconds storage, big endian "

    @Enumerator Wie werden die Daten in die Datenbank geschrieben? Mit dem selben Programm, mit dem die Daten auch wieder ausgelesen werden oder werden die Daten durch ein anderes Programm geschrieben?



  • Der Fractional-Part ist aber für die Stunden irrelevant.



  • Die MFC bietet scheinbar nur "RFX_Date" als Funktion um die Zeit auszulesen in den Varianten...

    https://docs.microsoft.com/de-de/cpp/mfc/reference/record-field-exchange-functions?view=msvc-160#rfx_date

    ...mit CTime, TIMESTAMP_STRUCT und COleDateTime.

    Geschreiben werden die Daten mit dem selben Programm per "INSERT (...) VALUES (..., '26:30:00', ...)".

    @firefly
    Es gibt leider keine "RFX_Time" Funktion:

    RFX_Binary Überträgt Byte-Arrays vom Typ CByteArray.
    RFX_Bool Überträgt boolesche Daten.
    RFX_Byte Überträgt ein einzelnes Byte an Daten.
    RFX_Date Überträgt Zeit- und Datumsdaten mit CTime oder TIMESTAMP_STRUCT.
    RFX_Double Überträgt Gleitkommazahl mit doppelter Genauigkeit.
    RFX_Int Überträgt Ganzzahldaten.
    RFX_Long Überträgt lange Ganzzahldaten.
    RFX_LongBinary Überträgt BLOB-Daten (Binary Large Object) mit einem Objekt der CLongBinary Klasse.
    RFX_Single Überträgt Gleitkommadaten.
    RFX_Text Überträgt Zeichenfolgendaten.

    PS: Es gibt neben der TIMESTAMP_STRUCT auch eine TIME_STRUCT aber diese lässt sich nicht per RFX_Date verwenden.



  • Sowohl CTime als auch TIMESTAMP_STRUCTunterliegen den Einschränkungen des gregorianischen Kalenders.

    Wenn dein Programm die Daten doch selber schreibt, kannst du doch die Zeitangaben >24h selber in Tage umrechnen.
    Du hast leider immer noch nicht geschrieben, warum du überhaupt solche Zeitangaben benötigst...

    PS: Wenn du mit SQL im Code hantierst, solltest du unbedingt Datenbank-Parameter (SQLBindParameters) benutzen!



  • Ich will aber keinen Tag bzw. DateTime haben. Die Datenbank hat eine TIME Datentyp Spalte. Da kann ich keinen Tag speichern. Vielleicht kommt man über RFX_Binary an den rohen Wert ran? Weis jemand wie die Daten des MySQL TIME Datentyps repräsentiert werden?

    CDBVariant var;
    rs.GetFieldValue(0, var, SQL_C_BINARY);
    CLongBinary* pBinary = var.m_pbinary;
    BYTE* pData = (BYTE*)GlobalLock(pBinary->m_hData);
    ...
    


  • Öhm könntest du bitte alle meine Fragen beantworten?
    Unter anderem: Enthält das Date objekt die korrekten werte? Hat aber nur zusätzlich Werte gesetzt für das Datum

    Ansonsten. Du schreibst es als string. Dann lies das als string wieder ein und parse das ganze passend



  • @Enumerator sagte in CRecordset::GetFieldValue mit SQL_C_TIME:

    Ich will aber keinen Tag bzw. DateTime haben. Die Datenbank hat eine TIME Datentyp Spalte. Da kann ich keinen Tag speichern. Vielleicht kommt man über RFX_Binary an den rohen Wert ran? Weis jemand wie die Daten des MySQL TIME Datentyps repräsentiert werden?

    Ich möchte iwas in einer Datenbank speichern, für das es keinen dedizierten Datentyp gibt. Also habe ich was genommen, was so ähnlich aussieht, aber das Zurücklesen klappt nicht. Was kann ich tun?



  • @Enumerator sagte in CRecordset::GetFieldValue mit SQL_C_TIME:

    Jetzt ist es mir nicht möglich gewesen mittels "RFX_DATE" Werte größer als 24 h auszulesen,
    denn im DBVariant.m_pdate->year/month/day steht völlig schwachsinnigerweise das aktuelle Datum drin.
    Wer denkt sich so einen Misst aus...

    Schlaue Leute denken sich sowas aus. Nur weil du es für Mist hältst ist es noch lange kein Mist, das macht schon alles Sinn. (Also was keinen Sinn macht ist dass die DB nen INSERT mit 26:xx:xx überhaupt annimmt - der sollte gleich von vornherein einen Fehler werfen. Was aber Sinn macht ist keine ungültigen Zeitwerte zu akzeptieren.)

    Weis jemand wie man performant an den internen Wert (die Ticks) kommt?
    Ich gehe mal stark davon aus, dass MySQL die TIME nicht als Chars abspeichert.
    Ansonsten wären sicherlich keine performanten Zeitvergleiche möglich.

    An die Ticks wirst du vermutlich gar nicht kommen.

    Nimm einfach einen SQL_C_LONG. Natürlich musst du dann die Konvertierung String <-> LONG selbst implementieren. Aber das ist ja wohl keine Hexerei. Dafür ist es dann performant - also die Vergleiche auf dem rohen Integer.



  • @hustbaer sagte in CRecordset::GetFieldValue mit SQL_C_TIME:

    (Also was keinen Sinn macht ist dass die DB nen INSERT mit 26:xx:xx überhaupt annimmt - der sollte gleich von vornherein einen Fehler werfen. Was aber Sinn macht ist keine ungültigen Zeitwerte zu akzeptieren.)

    Doch macht sinn, denn der TIME datentyp hat eine range von -838:59:59 bis 838:59:59

    https://dev.mysql.com/doc/refman/8.0/en/time.html

    Der TIME Datentyp ist nichts anderes vom prinzip her wie der Timespan Datentyp in C#



  • @firefly
    Ja, sorry. Bei einem "datetime" Datentyp macht es keinen Sinn. Bei einem reinen "time"/"duration" Datentyp natürlich schon.
    OP hat es halt mit einem "datetime" Datentyp versucht und sich dann aufgeregt dass es nicht geht.



  • Lest doch mal was ich geschrieben habe. Ja wenn ich es als DateTime einlese hat es den "korrekten" Zeitstempel, aber bei > 24h fehlen halt 24h (25:30 -> 01:30). Am Datum kann man den zusätzlichen Tag aber nicht erkennen. Und ich weis dass es einen TIME Datentyp gibt. Aber es gibt halt nunmal in der MFC keine Möglichkeit einen solchen abzufragen. Es gibt nur die RFX_Date Funktion zum Datenaustausch. Und dass ich wenn ich den Zeitstempel als LONG auslese nur die Stunde bekomme habe ich auch schon geschrieben. Also wir drehen uns im Kreis bzw. ihr wisst offensichtlich auch keine andere Lösung als es als String auszulesen. Besonders Kurios. Man kann das TIME Feld auch als Integer setzen (= '12:30:00' vs. = 123000). Aber man kann es nicht mehr auslesen. Und selbstverständlich ist das Murks. So wie die gesamte MFC Murks ist. Schrott aus einem anderen Jahrtausend.



  • @Enumerator sagte in CRecordset::GetFieldValue mit SQL_C_TIME:

    Und dass ich wenn ich den Zeitstempel als LONG auslese

    Ich meinte du sollst es gleich in einem 64 Bit Integer Feld in der DB speichern.

    Also wir drehen uns im Kreis bzw. ihr wisst offensichtlich auch keine andere Lösung als es als String auszulesen.

    Was gehen sollte ist ne Dynamic Query zu machen und das Feld im SQL Query String in z.B. Sekunden oder Millisekunden zu konvertieren. Die so erzeugte Spalte kannst du dann an ein Integer Feld binden. Dabei brauchst du dann halt zwei verschiedene Recordset-Klassen, eine zum Schreiben und eine zum Lesen. Also auch irgendwie nicht schön.

    RFX mit CTime wirst du ja wahrscheinlich schon probiert haben. Wenn du in der DB ein DATETIME Feld machst muss das auf jeden Fall gehen. Dann suchst du dir halt ein bestimmtes Null-Datum aus (Epoch), und speicherst deinen TimeSpan Wert als Datum ausgehend von diesem Null-Datum (Epoch + TimeSpan). Wenn du als Epoch das selbe Datum verwendest wie CTime, dann bekommst du mit RFX in ein CTime Objekt direkt eine Sekunden-Anzahl die du nicht weiter umrechnen musst.

    So wie die gesamte MFC Murks ist. Schrott aus einem anderen Jahrtausend.

    Würde ich so nicht sagen. Die MFC ist nicht gerade modern und hat ein paar komische Eigenheiten, ist aber grundsätzlich schon ein solides Application-Framework. Was nicht heisst dass die ODBC Klassen der MFC besonders toll sind. Ich habe immer ADODB verwendet. Mit dem COM-Support von MSVC (#import) geht das schon recht gut. Ich kann mich nicht erinnern damit unlösbare Probleme mit irgendeinem Datentyp gehabt zu haben - aber es ist auch recht lange her. Das Handling von DATETIME Spalten in ADODB ist auch komisch, die werden einfach als double (Sekunden seit Epoch) Transportiert. Da du bei ADODB keine Eigene Klasse pro Tabelle/View brauchst, ist es mit ADODB aber auch nicht so schlimm wenn du Lesen und Schreiben einer Tabelle unterschiedlich machst. Dann kannst du z.B. zum Lesen in Millisekunden konvertieren und beim Schreiben das Format deiner Wahl mit CONVERT in den passenden Feld-Typ konvertieren. Damit verlierst du dann natürlich die DB-Unabhängigkeit, weil du auf einen bestimmten SQL Dialekt gebunden bist. Ist aber mMn. für die meisten Projekte egal, weil es bei den meisten Projekten keine Rolle spielt wenn man eine bestimmte DB vorgibt.


Log in to reply