ODBC - Werte in Datenbank schreiben
-
Hi Leute!
Ich frickel hier gerade eine kleine Mini-Anwendung zusammen, die Werte in die Datenbank schreiben soll. Dazu verwende ich die MFC Klasse "CDatabase", die einem ja recht einfach Zugriff auf ODBC Datenquellen verschafft.
Das Ergebnis einer Abfrage lässt sich schön in einem Recordset-Objekt verwalten und auslesen. Ich frage mich allerdings, wie das mit dem schreiben/editieren/löschen von Daten funktionieren soll ?!
So wie ich das Konzept verstanden habe, leitet man sich eine Klasse von CRecordset ab und definiert schon hier die Datenbank-Tabelle für die dieses Recordset sein soll. Anschliessend legt man für jede Tabellen-Spalte einen Member an. Der Datenaustausch passiert dann über diese DoFieldExchange FunktionenWie dämlich ist das denn bitte ? Geht das nicht dynamischer ? Was muss ich machen, damit ich a) Nur eine Recordset-Klasse für alle Tabellen/Views whatever brauche und b) Ich nicht auf diese bekloppten Tabellenfeld-Member angewiesen bin.
plz help!
-
Schau dir mal die DTL an.
Die bietet mehr Möglichkeiten als die MFC Klassen.
http://dtemplatelib.sourceforge.netDevil
-
kannst doch alles dynamisch machen! Aber die andere Frage ist was brigt es dir. schreibe den Inhalt eines Editfeldes in eine Tabelle die ich noch nicht kenne in eine Spalte die bald erzeugt wird.
Also ich versteh nicht was du haben möchtest, bzw weiß ich nicht was mit CDaabase und CRecordset nicht dynamisch gehen soll!!
-
Wieso spalten die ich nicht kenne ?!
Wenn du eine Tabelle über CRecordset Open öffnest bekommst du über GetODBCFieldInfo alle Felder + Datentypen und sonstige Eigenschaften.
Habs jetzt übrigens einigermaßen hinbekommen. Allerings bekomme ich beim Update() immernoch eine Access-Violation. Muss mich da mal durch-debuggen.
-
So, der Source liefert schonmal keine offensichtlichen Fehler mehr. Dennoch kommen meine Daten einfach nicht in die DB
Ich hab mich mal "durch-debuggt". Bis zur Update() Funktion läuft alles gut. Bei Edit() werden noch die Feldinhalte aus der DB sauber in meine dynamische Feld-Struktur übernommen. Dann ändere ich den Wert eines Feldes um und rufe Update() auf um diese Änderung in die Datenbank zu übernehmen. Und hier ist das Problem. Wenn ich den Update() Source mit dem Debugger verfolge lande ich irgendwann bei BuildUpdateSQL() der Klasse CRecordSet. Hier sollte das SQL Statement für das Update() zusammengesetzt werden. Guckt man sich in der Variablen-Überwachung an was die Funktion dort zusammenbraut, so ergibt sich das folgende sinnvolle Statement "update texte set text = ? where current of cursor0"
Dieses Statement wird in der Tat an die Datenbank geschickt und ohne Fehler (!) ausgeführt. Natürlich wollte ich in das Feld text aber nicht "?" sondern "test123" schreibenWo ist mein Wert geblieben ?
Nach weiterem Debuggen kam ich an die Stelle, wo normalerweise wohl der Wert eingesetzt werden sollte. Diese sieht so aus:if(!(m_prs->m_dwOptions & CRecordset::optimizeBulkAdd)) { *m_pstr += '?'; *m_pstr += m_lpszSeparator; m_nParamFields++; // Assumes all bound fields BEFORE unbound fields CODBCFieldInfo* pODBCInfo = &m_prs->m_rgODBCFieldInfos[nField - 1]; AFX_SQL_SYNC(::SQLBindParameter(m_hstmt,(UWORD)m_nParamFields, SQL_PARAM_INPUT, (SWORD)nCType, pODBCInfo->m_nSQLType, pODBCInfo->m_nPrecision, pODBCInfo->m_nScale, pv, 0, plLength)); if (nRetCode != SQL_SUCCESS) m_prs->ThrowDBException(nRetCode, m_hstmt); }
Hier wird das olle "?" eingesetzt. Anschliessend kommt SQLBindParameter. Was genau bewirkt diese Funktion ? Mit dem SQL Statement passiert nach verlassen der Funktion jedenfalls nichts.
Was mach ich falsch ? Wo könnte der Fehler stecken ?
-
Ich hab mich da auch mal durchdebuggt.
Die ? sind Platzhalter für die Feldwerte und ich glaube, man konnte nicht in die Funktion reingucken, wo die ersetzt werden.
Bastel dir doch den Update-String selber und schick ihn runter.Nur für den Fall, dass es gar nicht geht.
Mich hatte es ganz zu Anfang auch sehr genervt, dass man für jede Tabelle eine Klasse machen muss. Mittlerweile habe ich mich damit abgefunden und kann damit auch gut leben.
-
Das kommt gar nicht in Frage, das ich pro Tabelle eine Klasse bastel
Wenn ich das Update manuell abschicke bekomm ich übrigens auch ne Accesss-Violation.
Ach ja, ich hab mal an der Stelle geguckt wo die Platzhalter ersetzt werden sollten. Dort kommt zwar die richtige Variable aus dem Recordset rüber aber trotzdem tauscht er das ? nicht durch den Variablen-Inhalt aus
Müssen die Variablen in RFX_Text auf einen bestimmten Datentyp gecastet sein damit die Funktion was damit anfangen kann ? Ich übergebe CString, so wie ja eigentlich auch in der Funktion vorgesehen.
-
Nur mal so, um meinen Lieblingsfehler auszuschließen:
Ist das Recordset offen?Funktioniert es denn überhaupt, wenn du das Set an eine Tabelle knüpfst? (Mit eben gerade den nötigen Änderungen?)
Sonst ist der Fehler woanders.
-
Hm, hab das ganze nochmal mit einer vom Klassen-Assi generierten CRecordset "hardgebundenen" Klasse getestet und es geht. Scheint wohl an meiner dynamischen CRecordset-Klasse zu liegen
Das Problem ist wahrscheinlich, das ich zum Zeitpunkt des Open() nicht weiss, wie viele Felder ich bekomme und pauschal 255 übergebeBis zum Update()funktioniert das sogar
Ich müsste irgendwie vorher rausfinden, wie die Tabellenstruktur aussieht.
Wie lautet der Befehl unter SQL ? Unter Oracle geht es mit "desc [TabellenName]". Aber das scheint kein Standard-SQL zu sein.
Und selbst dann, bekomme ich nur Infos über eine Tabelle. Was ist, wenn ich einen Join abfrage ? Oder einen View ?
ODBC fängt an, mich anzunerven
-
Wann könntest du das denn mit dem Recordset herausfinden?
Kann man überhaupt ein Update in einem Joined-View machen? (Ich glaub, da war ein Haken...)Ich hab hier einen MS-SQL Server, da würde ich dich mal in deiner Datenbank in die syscolumns-Tabelle gucken lassen.
Es MUSS irgendwo eine Systemtabelle geben, wo das drin steht.
-
Nein, einen Joint-View kann man natürlich nicht updaten. Aber um die Daten daraus einigermaßen plausibel ins Recordset zu bekommen muss man ja vorher wissen, wie viele Felder da auf einen zukommen.
-
Geht denn ein Select auf die zu-updatende Tabelle?
Und kannst du dann herausfinden, wieviele Felder die hat?
Dann würde ich das vorher einfach abfragen und dann mit den korrekten Daten erneut abfragen.Oder eben mit einem "select count(wasauchimmer) from syscolumns where ???" oder so ähnlich.
Oder, als endgültige Krücke würde ich solche Daten irgendwo speichern, in einer Tabelle z.B. wo nur Tabellenname und Feldanzahl drinsteht.
Die kannst du zum Start einmal abfragen und weißt dann eben alles.
-
Hm, neues Phänomen
Wenn ich die Tabelle mit "select * from tabellenname" öffne und dann mit Edit() und Update() einen Wert speichere funktioniert es plötzlich!
Wenn ich aber eine "Where-Bedindung" anhänge, oder mit MoveNext einen Satz weiter-skippe bekomme ich beim Schreiben den Fehler "Es wurden mehrere Zeilen aktualisiert". Was ist das nun wieder ?
-
Die Tabelle hat keinen Primärschlüssel.
-
Hm, ich dachte dafür wäre ein Cursor da ?!
Jetzt gehts! Auch mit where und dynamischer Recordset-Klasse und allem anderen!Danke leute!
-
Hallo nochmal!
Suchst du noch den desc Befehl? Und verwendest MS-SQL Server?
Dann hab ich was gefunden. Hab hier ein Buch vor mir, da steht das drinne.
Transact-SQL | ISBN: 3827319242ab S. 521
-
desc?
War doch gar nie gefragt!
-
Cpp_Junky schrieb:
Ich müsste irgendwie vorher rausfinden, wie die Tabellenstruktur aussieht.
Wie lautet der Befehl unter SQL ? Unter Oracle geht es mit "desc [TabellenName]".
-
*gg* nimm ja schon alles zurück!
-
estartu_de schrieb:
...
Suchst du noch den desc Befehl? Und verwendest MS-SQL Server?
...
Transact-SQL | ISBN: 3827319242ab S. 521Ne, ich verwende zum testen FireBird. Eigentlich aber soll die Anwendung mit allen ODBC Datenbanken verwendbar sein. Deshalb brauch ich irgendeinen SQL-konformen Standard-Befehl für sowas. Gibts den ?