Tatsächlich ausgeführter Update-SQL Befehl

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 23 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    Tatsächlich ausgeführter Update-SQL Befehl

    Hallo Allerseits,

    meine Frage an Euch: "Weiss jemand, wie man den intern tatsächlich ausgeführten SQL Befehl abfragen kann ?" - also was wird wirklich auf die SQL Engine einer beliebigen Datenbank geschickt.

    Was ich damit meine anhand eines kleinen Testprogramms:
    ( Daten einlesen, Werte verändern, Update auf die Datenbank)...

    VB.NET-Quellcode

    1. Private Sub TESTEN2()
    2. Dim DS As System.Data.DataSet
    3. Dim DT As System.Data.DataTable
    4. Dim DR As System.Data.DataRow
    5. Dim DA As System.Data.Odbc.OdbcDataAdapter
    6. Dim CB As System.Data.Odbc.OdbcCommandBuilder
    7. ' --- DataSet erzeugen
    8. DS = New System.Data.DataSet()
    9. ' --- Adapter definieren
    10. DA = New Odbc.OdbcDataAdapter("Select * from Liste", sConnectionString)
    11. ' --- SQL-DML-Befehle erzeugen
    12. CB = New Odbc.OdbcCommandBuilder(DA)
    13. ' --- Tabelle einlesen
    14. DA.Fill(DS, "Liste")
    15. ' --- Zugriff auf Tabelle
    16. DT = DS.Tables("Liste")
    17. DR = DT.Select(" NR= 4")(0)
    18. DR("ZeitpunktRefresh") = Now
    19. ' --- Speichern der Änderungen
    20. ' ****** die Folgende Zeile ist der entscheidene Punkt meiner Frage: Was passiert hier intern tatsächlich??
    21. DA.Update(DS, "Liste")
    22. DA.Dispose()
    23. DS.Dispose()
    24. End Sub


    Dann wird ja in "DA.Update(DS, "Liste")" einem als Programmierer alle Arbeit abgenommen. Aber manchmal gibt es Fehlermeldungen, die man nicht zuordnen kann. (welche sei mal dahingestellt).
    Gern würde ich wissen, welches *exakte* SQL Befehls Syntax dann auf die Datenbank geschickt wird.
    Beispielsweise müssen bei einem Update Statement bei Access oder bei MySQL die Datumsangaben anders formatiert übergeben werden. Das klappt auch meistens, aber ich würde gern wissen, was wirklich intern verarbeitet wird.

    Mein einer Ansatz war, dass man das Ereignis "meinAdapter_RowUpdating" ausliest.
    Dort findet man dann unter "UpdateCommand" soetwas:

    UPDATE Liste SET ZeitpunktRefresh = ? WHERE ((NR = ?) AND ((? = 1 AND ProgFunktion IS NULL) OR (ProgFunktion = ?)) AND ((? = 1 AND ProgUser IS NULL) OR (ProgUser = ?)) AND ((? = 1 AND RechnerName IS NULL) OR (RechnerName = ?)) AND ((? = 1 AND Zeitpunkt IS NULL) OR (Zeitpunkt = ?)) AND ((? = 1 AND VollSperre IS NULL) OR (VollSperre = ?)) AND ((? = 1 AND Aktiv IS NULL) OR (Aktiv = ?)) AND ((? = 1 AND ZeitpunktRefresh IS NULL) OR (ZeitpunktRefresh = ?)))

    Erstens finde ich dieses Statement recht verwirrend und zweitens ist das noch nicht die finale Formatierung z.B. bei "ZeitpunktRefesh = ?" - da müssen bei der JetEngine noch die Rauten davor oder bei MySQL noch Anführungszeichen.

    Weiss jemand, wo man diese internen Detailinfos findet?
    Danke für hilfreiche Hinweise ...

    CodeTags korrigiert; bitte zukünftig darauf achten, das richtige CodeHighlighting zu verwenden ~VaporiZed

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „VaporiZed“ ()

    Vollzitat des direkten Vorposts an dieser Stelle entfernt ~VaporiZed

    Ja, das sind Prepared Statements. die "?" werden dann durch meine Werte ersetzt.
    Aber wie und wo finde ich das Ergebnis am Ende?
    Beispielsweise...wie wird das Datum nun wirklich eingefügt? Also der fertige SQL-Statement Text.

    ----

    Aktuell kämpfe ich mit dem Problem, dass mir die Datenbank meldet "Out of range Value for Zahlbetrag". Dabei sind das ganz kleine Euro Beträge. Da vermute ich, dass das Datenbankfeld ein Maria-DB Decimal Feld ist, aber der Wert ist "glatt" also ohne Nachkommastellen. Und so wird er übergeben. Und das scheint irgendwo zu stören...

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „VaporiZed“ ()

    Vollzitat des direkten Vorposts an dieser Stelle entfernt ~VaporiZed

    Ja, danke für den Hinweis.
    An der STelle bekomme ich aber auch nur prepared Statements:
    INSERT INTO Liste(ProgFunktion, ProgUser, RechnerName, Zeitpunkt, VollSperre, Aktiv, ZeitpunktRefresh) VALUES (?, ?, ?, ?, ?, ?, ?)

    Was mich interessiert: Wenn man z.b. drei neue Datensätze angelegt hat... welche Statement (mit den richtigen Werten) wird für Datensatz 1, Datensatz 2 und Datensatz 3 generiert.

    Wie gesagt, ich hab das Problem, dass ein Decimalwert nicht in die Datenbank schreibbar ist. Und ich muss irgendwie herausfinden, wo es in der ganzen Hintergrundverarbeitung hakt.
    Aktuell versuche ich gerade über die internen Server-Logfiles der MariaDB herauszufinden, was VB.Net wirklich intern generiert.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „VaporiZed“ ()

    rolliproggi schrieb:

    An der STelle bekomme ich aber auch nur prepared Statements
    Du bekommst DbCommand-Objekte.
    Ja - das sind "prepared Statements". Aber nenn es wie du willst.
    Auf jeden Fall hat so ein DbCommand-Objekt eine Parameters-Auflistung, und da kannst du im Lokal-Fenster jeden einzelnen Parameter angucken, und welcher Db-Datentyp ihm zugeordnet ist.



    rolliproggi schrieb:

    Was mich interessiert: Wenn man z.b. drei neue Datensätze angelegt hat... welche Statement (mit den richtigen Werten) wird für Datensatz 1, Datensatz 2 und Datensatz 3 generiert.
    Es ist immer dasselbe "Statement" (noch einmal: es ist ein DbCommand) - und du hast es ja schon gesehen - zumindest den CommandText.
    Die Parameter dieses Statements werden mit den Werten aus Datensatz 1, Datensatz 2 und Datensatz 3 befüllt, und dann das DbCommand ausgeführt.
    prepared Statement eben (wenn du so willst).

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Ja, ich weiss. Vielen Dank für diese Antworten.
    Man kann dort alle Parameter fein säuberlich aufgelistet sehen.

    Aber was wird wirklich auf die Datenbank geschickt?

    Am Ende muss ja ein Befehl wie
    INSERT INTO `Liste` (`NR`, `ProgFunktion`, `ProgUser`, `RechnerName`, `Zeitpunkt`, `VollSperre`, `Aktiv`, `ZeitpunktRefresh`) VALUES
    (1, 'Fakturierung', 'Vxx', 'DESKTOP-PC', '2019-10-11', '', '1', '2019-10-11 00:00:00')

    Über die Leitung gehen. Und ich würde gern wissen, wie die einzelnen Felder intern wirklich behandelt werden. Mit allen Feinheiten.
    Wird der Wert "15" wirklich als 15 übergeben oder als 15.00 etc.

    rolliproggi schrieb:

    Am Ende muss ja ein Befehl wie
    INSERT INTO `Liste` (`NR`, `ProgFunktion`, `ProgUser`, `RechnerName`, `Zeitpunkt`, `VollSperre`, `Aktiv`, `ZeitpunktRefresh`) VALUES
    (1, 'Fakturierung', 'Vxx', 'DESKTOP-PC', '2019-10-11', '', '1', '2019-10-11 00:00:00')
    Über die Leitung gehen.
    Nein.
    Es geht der Befehl über die Leitung, dessen CommandText du schon gesehen hast.
    Mit ihm zusammen werden die Parameter transportiert, aber keinesfalls eingefrickelt in den CommandText, wie du dir das scheinbar vorstellst.

    Im EntityFramework kann man vergleichbares loggen, Log-Einträge mögen in etwa so formuliert sein:

    SQL-Abfrage

    1. INSERT INTO Liste(ProgFunktion, ProgUser, RechnerName, Zeitpunkt, VollSperre, Aktiv, ZeitpunktRefresh) VALUES (?, ?, ?, ?, ?, ?, ?)
    2. -- @p0=1, int
    3. -- @p1='Fakturierung', varChar(50)
    4. -- @p2='Vxx', varChar(50)
    5. -- @p3='DESKTOP-PC´, varChar(50)
    6. -- @p4= '2019-10-11', smallDateTime
    7. -- @p5=1, bit
    8. -- @p6='2019-10-11 00:00:00', dateTime
    Aber das ist nur die SqlServer-Versinnbildlichung für Debug-Zwecke. In wirklichkeit gehen iwelche Bytes durch die Leitung - ich weiss nicht wie formatiert.
    Ist auch völlig egal.
    Für dich wichtig ist, was deine Parameter als DbDatentyp eingetragen haben. Wenn da int eingetragen ist, und in deim Dataset ist die Spalte aber Long, oder Boolean oder was, dann kann es zu Mismatches kommen.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Vielen Dank für deine Mühe & Geduld.

    Wollte gerade paar Screenshot Ausschnitte hochladen...aber da gehen wohl nur Links.
    Darum hier kurze inhaltliche Beschreibung.

    Das Feld ist in MariaDB als "Decimal (15,2)" definiert.
    Der parameter (bei mir ist es Nr 9) hat eine "Value = 5 / Object {Decimal}", Bei "Size" steht 0 (vielleicht liegt da der Fehler???), Bei OdbcType steht "Decimal {6} vom Typ System.Data.Odbc.OdbcType".

    Parallel versuche ich gerade die Logdatei auszulesen. Gefunden habe ich die MariaDB Log schon...aber ich hab noch keine Zugriffsrechte...
    Nicht mein Tag heute ;)

    ----
    Vielleicht noch eine Info zum Hintergrund:
    Ich bin dabei ein existierendes Programm von MS Access auf MariaDB zu migrieren. Theoretisch sollte man ja nur den Connection-String austauschen und alles funktioniert wie "von selbst".
    Die Verbindung klappt soweit auch.
    Nur beim Insert hab ich jetzt dieses Problem.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „rolliproggi“ ()

    Das scheint mir doch ok zu sein.
    Der Wert 5 wird sicherlich korrekt in die Db übernommen.



    Was Parameter.Size bedeutet weiss ich auch nicht. Vielleicht in diesem Falle die Anzahl nachkommastellen.
    In der Doku (ObjectBrowser) steht, dass Size defaultmässig vom Parameter-Wert abgeleitet wird (was immer die damit meinen mögen).



    Übrigens das gestelzte UpdateCommand aus post#1 - das macht der CommandBuilder so, um Pessimistic Locking umzusetzen.
    Du kanns cb.ConflictOption = LastInWins setzen, dann wird das Command wesentlich simpler - um Locking musst du dich dann aber selber kümmern.



    rolliproggi schrieb:

    Wollte gerade paar Screenshot Ausschnitte hochladen...aber da gehen wohl nur Links.
    Ich hatte da noch nie Probleme.
    Nutze die Datei-Anhang-Funktion des Forums (erweiterte Antwort/Bearbeitung) - bitte keine Links auf iwelche Datenkraken-Fremdhoster.
    Der Tipp mit "cb.conflictoption = LastInWins" ist sehr gut. Danke dafür!

    Mittleweile bin ich an die Logdateien der MariaDB rangekommen. Ab in die Tiefen von Linux..
    Und was ist?
    Da stehen die selben blöden prepared Statements drin....
    Ich bleib weiter dran, wie man diesen Datenbankfehler lösen kann.

    Eines kann ich sagen: Wenn man das Datenbankfeld von "decimal (15,2)" auf "float" ändert, dann geht es...

    --------------------
    @ErfinderDesRades
    Hab über die server Logdateien etwas herausgefunden!!
    die Prepared Statements werden (wie ich vermutet hatte) doch zu einem "richtigen" SQL Statement zusammengebaut!

    Scheinbar wird aber über den Datenbanktreiber (so wie Du gesagt hast) wirklich die Rohdaten gesendet und dann im Server der Insert Befehl aufgebaut.
    In den Server Logdateien findet man dann sowohl das Prepared Statement als auch den korrekten Befehl in SQL Syntax.

    Und hier fand ich auch den Grund, warum es wohl nicht geht:
    Mein Decimal Feld wird eingefügt als

    VALUES (18, 5, 44, 94, DATE'2022-05-01', 'so ein Mist!', 111, 10000, 0, 5, 43.9900016784668, '000000000000000', '000000000000000', '000000000000000', 1, NULL, NULL, NULL, 89, '000000000000000', 'Kurze Straße 4', '71199', 'Stuttgart', 'D', NULL, NULL, NULL, NULL, 0, 0, 0, NULL, NULL, 0, '000000000000000', '000000000000000', 1, NULL, NULL, NULL, 1, '', '000000000000000', '43.99', 0, DATE'0001-01-01', 1, NULL, NULL, 3, DATE'2022-06-19', DATE'2022-06-19')

    Eingegeben hatte ich 44 Euro.
    Und dieses ganzen Nachkommastellen werden dann abgeschnitten bzw. da meckert die Datenbank zurecht, dass es nicht geht

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „rolliproggi“ ()

    Moinsen,

    entschuldigt, wenn ich das mal so drastisch ausdrücke, aber das kommt davon, wenn man den ganzen Mist die Designer machen läßt.

    Vernüftige Datenklassen schnell selbst erstellen, eine saubere Datenzugriffsschicht, wo die Sql-Abfragen sauber hinterlegt sind (dann weiß man auch, was übertragen wird), natürlich parametrisiert. Und im Idealfall handelt es dich bei den Abfragen dann noch um SPs, die man ggf. im DBMS auch noch debuggen kann.

    Und schon fallen ein Großteil der Probleme einfach in sich zusammen.

    Viele Grüße

    Gerrit
    ich sehe nicht, was das mit generierten Datenklassen zu tun hat. Mit selbstgebastelten Datenklassen wäre das Problem doch dasselbe.
    An DbCommands mit Parametern geht kein Weg dran vorbei, weil alles andere ist Einladung für SqlInjection-Angreifer.

    Man muss sich scheints darüber wundern und mit auseinandersetzen, dass bei MariaDB Currency-Werte nicht als decimal deklariert werden müssen sondern als float.
    Bringt mich auf die Frage: Kennt MariaDb vielleicht einen currency-Datentyp? Der wäre für Geld-Werte besser angemessen als float.

    Auf der DotNet-Seite hingegen gehören Geld-Werte als Decimal deklariert.
    Wenn man alles selbst programmiert, dann kann man bei solchen Fehlern halt eingreifen.
    So passiert alles im Hintergrund und keiner blickt mehr durch.
    Jetzt habe ich statt "decimal" einfach float genommen und die Werte sehen in der Datenbank soweit gut aus.

    Hatte früher schon Projekte mit MySQL - da haben wir alle Datenbankupdates als Command-Objekte selbst gebaut. Wenn was nicht funktionierte weil irgendeine Syntax nicht passte, dann hat man das halt umprogammiert. Das lief ausgesprochen robust.
    Heute läuft alles "akademisch" und nix funktioniert so richtig.

    Grundsätzliches Problem ist, dass die VB.Net / ODBC / MariaDB Welt wohl in der Konstellation nicht getest wird. Jeder kocht seine eigene Suppe und am Ende schmeckt das Menue nicht.

    Aber vielen Dank für all Eure Hinweise - gerade Dein Tip von "ErfinderdesRades" mit den vereinfachten Command-Befehlen wird mir wohl gut weiterhelfen.
    mir ist noch was eingefallen: Du verwendest ODBC-Command/CommandBuillder/DataAdapter für MariaDb.
    Warum nicht die Original-MariaDb - Infrasstruktur?
    Vielleicht verursacht das ja irgendwie diesen unerfreulichen Decimal/decimal(15,2) - Mismatch.
    Ich wäre jdfs. unzufrieden damit, den .Net-Datentyp Decimal auf Sql-float mappen zu müssen. (Es sei denn, genau diese Wunderlichkeit wäre iwo dokumentiert).
    Decimal ist erfunden worden, um die Rundungsfehler abzuschaffen, die bei float/single/double und konsorten entstehen, wenn man zB durch 10 teilt (was ja im Dezimalsystem das allernormalste ist). Auf dieses notwendige Feature sollte man nicht verzichten.
    Also ich hab meine Anwendung unter anderem für MySQL vorbereitet und natürlich getestet - mit MariaDB. Ich hab mit den Decimal-Sachen absolut keine Probleme...

    VB.NET-Quellcode

    1. Case GetType(Decimal)
    2. Select Case _provider
    3. Case "MySQL" : dbDependentName = "DECIMAL(10,2)"
    4. Case "MsSQL" : dbDependentName = "numeric"
    5. Case "Access" : dbDependentName = "Double"
    6. End Select


    Schau' mal nach, ob deine Datentypen tatsächlich übereinstimmen.
    EDIT: achso, was ab und an vor kommt: Ganze Zahlen (Beträge) - z.B. 10,00 werden erst nach Abspeichern und neu aus der Datenbank laden als 10,00 angezeigt - vorher als 10.
    Ist aber vermutlich eher was mit der Formatierung im DataGridView.

    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:
    @ErfinderDesRades:
    "Du verwendest ODBC-Command/CommandBuillder/DataAdapter für MariaDb.
    Warum nicht die Original-MariaDb - Infrasstruktur?"

    Da wollte ich ganz bewußt die "tollen Features" der modernen Programmierung nutzen: Nur der Connection-String bestimmt, mit welcher Datebank man arbeitet. Das Programm selbst ist Basis-Unabhängig.
    Sinn: Das Programm läuft bisher auf Access. Testweise soll es jetzt mal unter Maria DB laufen. Und so die Theorie - man soll nur den Konfig String ändern und alles läuft super weiter.

    Später kann man dann darüber nachdenken, eine spezielle EXE nur für Maria-DB zu erstellen. Für den Moment soll es nur eine EXE sein.

    @tragl
    Sehr interessant was Du schreibst. Leider kann ich Deinen Code nicht übertragen auf mein Problem.
    FEhlerbild bei mir: In der MariaDB ist ein Feld "decimal (10,2)" und ich schreibe in ein DataTable / DataRow für dieses Feld den Wert 5 ( Variable ist double).
    Und wenn ich das dann in die Datenbank schreibe, dann kommen die Fehler...

    Wie läuft das bei Dir? Wie umgehst du das? Welchen ODBC Treiber (Version ?) verwendest du?
    Kann sein, dass mein Treiber noch einen Bug hat.
    Ich hab mit verschiedenen Versionen getest und es zickt immer mal wieder an anderer Stelle...