Ich habe einige Agenten, die bisher nachts automatisch einige Statistikdaten aus Notes über eine ODBC Verbindung in eine MySQL Datenbank geschrieben haben. Jetzt wurde die Datenbank auf MS SQL Server migirert und nichts geht mehr :-\
Ich habe die entsprechende System DSN auf dem Domino Server neu eingerichtet, so dass jetzt unter gleichem Namen und mit den gleichen Credentials anstelle der MySQL Datenbank der MS SQL Server erreicht werden kann. Ein Verbindungstest im Assistenten zur Einrichtung der ODBC System DSN ist erfolgreich. Die richtige Vorgabedatenbank ist in den Einstellungen der ODBC Verbindung gesetzt.
Ich habe mir jetzt folgenden kleinen Agenten geschrieben, der die Arbeit mit der neuen Datenbank testen soll:
Sub Initialize
Dim ExportProfileDoc As NotesDocument
Dim session As New NotesSession
Dim dbCurr As NotesDatabase
Dim con As New ODBCConnection
Dim dsn As String
Dim user As String
Dim passwd As String
Dim qry As ODBCQuery
Dim result As ODBCResultSet
Set dbCurr=session.CurrentDatabase
Set ExportProfileDoc = dbCurr.GetProfileDocument("PROFILE XMLExport")
dsn = GetFieldValueTxt( ExportProfileDoc,"LeadExportSystemDSN")
user =GetFieldValueTxt( ExportProfileDoc,"LeadExportSystemDsnUser")
passwd = GetFieldValueTxt( ExportProfileDoc,"LeadExportSystemDsnPassword")
Set con = New ODBCConnection
Set qry = New ODBCQuery
Set result = New ODBCResultSet
Set qry.Connection = con
Set result.Query = qry
con.ConnectTo(dsn)
qry.SQL = "SELECT * FROM ADDRESSRECORDS"
If Not result.Execute Then
Messagebox result.GetExtendedErrorMessage,,result.GetErrorMessage
Exit Sub
End If
'If Not con.ConnectTo(dsn,user,passwd) Then
' Messagebox "Could not connect to " & dsn,, "Error"
' Exit Sub
'End If
'qry.SQL = "Select * from leadrecords"
'Set qry.Connection = con
'Set result.Query = qry
'result.Execute
'erst einmal Lesen üben
Dim i As Integer
result.LastRow
i = result.NumRows
result.addRow
Call result.SetValue("doctype","Provisionsabrechnung")
Call result.SetValue("lead_type","A")
Call result.SetValue("cust_salutation","Herr")
Call result.SetValue("cust_title","Dr.")
Call result.SetValue("cust_firstname","Walter")
Call result.SetValue("cust_lastname","Milchmann")
Call result.SetValue("cust_street","Am Milchberg")
Call result.SetValue("cust_streetnr","2b")
Call result.SetValue("cust_town","Dumpfhausen")
Call result.SetValue("cust_zip","34257")
Call result.SetValue("cust_phone1","5657/85858")
Call result.SetValue("cust_email","zappado@gmx.de")
Call result.SetValue("estate_type","Einfamilienhaus")
Call result.SetValue("estate_usage","")
Dim status As Variant
Dim ro As Variant
status = result.UpdateRow
ro = result.ReadOnly
result.Close(DB_CLOSE)
con.Disconnect
Print "Fertig"
End Sub
Bei dem momentan auskommentierten Teil erfolgt die Anmeldung vollautomatisch, bei der jetzt genutzten Methode muss ich das Passwort manuell eingeben. Die abgefragte Tabelle enthält ca. 200.000 Datensätze.
Was passiert nun bei der Ausführung des Codes:
- Ich werde zur Eingabe des Passworts aufgefordert
- Die Prüfung If Not result.Execute Then
ist erfolgreich, d.h. es erscheint keine Messagebox
- liefert -1
- status = result.UpdateRow
liefert false
- liefert ebenfalls false
Und nun stehe ich auf dem Schlauch. Obwohl zuerst offensichtlich erfolgreich eine ODBCConnection zur Datenbank aufgebaut wird, kann die Anzahl der Zeilen im result nicht ermittelt werden, und obwohl die Datenbank nicht readOnly ist, kann ich keine Datensätze hineinschreiben. Natürlich kann ich auch aus den Logs des SQL Servers nichts sehen. Dort erscheint noch nicht einmal ein Eintrag für den Versuch des Anmeldens durch meinen Benutzer.
Ich habe jetzt gar keinen Anhaltspunkt wie ich noch testen kann woran das Problem liegt. Hat jemand einen Vorschlag?
Danke im Voraus
Peter
IMHO liegt das Problem im Verbindungsaufbau.
Du hast schon ein "Dim con As New ODBCConnection" da ist das "Set con = New ODBCConnection" unnötig.
Weiter gehts dann mit
Set qry = New ODBCQuery
Set result = New ODBCResultSet
Set qry.Connection = con
Wieso weist du der ODBCQuery schon die Connection zu, obwohl Du diese noch nicht mal aufgemacht hast?
con.ConnectTo(dsn)
qry.SQL = "SELECT * FROM ADDRESSRECORDS"
Du machst ein connect, überprüfst nicht, ob das gut gegangen ist und wunderst Dich dann später, dass die Abfragen nicht funktionieren?
Hüstel.
Die Designer-Hilfe mein (leicht abgewandelt) dazu:
If Not con.IsConnected
....
End If
Wieso weist du der ODBCQuery schon die Connection zu, obwohl Du diese noch nicht mal aufgemacht hast?
..weil ich das für diesen Testagenten genau so aus der Notes Designer Hilfe entnommen habe, eben genau um mich da in der Reihenfolge garantiert nicht zu vertun. Siehe Stichwort: "Examples: Execute Method"
Du machst ein connect, überprüfst nicht, ob das gut gegangen ist und wunderst Dich dann später, dass die Abfragen nicht funktionieren?
Hüstel.
Auch das ist dem Testagenten geschuldet, in dem ich stattdessen prüfe ob das Execute Statement erfolgreich war. Mein "echter" Agent enthält sehr wohl eine Prüfung, aber das bringt mich nicht weiter:
If Not con.ConnectTo(dsn,user,passwd) Then
Messagebox "Could not connect to " & dsn,, "Error"
Exit Sub
End If
Ich habe mittlerweile noch etwas herumgespielt, und vielleicht einen Hinweis gefunden. Ich habe folgende kleine Ergänzung vorgenommen:
Set con = New ODBCConnection
con.SilentMode = True
Set qry = New ODBCQuery
Set result = New ODBCResultSet
Set qry.Connection = con
Set result.Query = qry
status= con.ConnectTo(dsn,user,passwd)
'status= con.ConnectTo(dsn)
'Es gibt zwei alternative Anmeldekommandos, je nachem ob ich die System DSN mit Windows Authentifizierung oder SQl Server Authentifizierung teste
qry.SQL = "SELECT * FROM LEADRECORDS"
If Not result.Execute Then
Messagebox result.GetExtendedErrorMessage,,result.GetErrorMessage
Exit Sub
End If
tables = con.ListTables(dsn)
If con.GetError <> DBstsSuccess Then
Messagebox con.GetExtendedErrorMessage,, con.GetErrorMessage
Exit Sub
End If
Ich setze also den silentMode der Connection auf "true" und öffne danach die Connection. (status = true danach).
Aber bei Aufruf der Zeile tables = con.ListTables(dsn)
werde ich plötzlich mit einem Anmeldedialog für die Datenbank konfrontiert, obwohl die Connection längst geöffnet sein sollte, und ich wegen con.silentMode=true nicht gefragt werden sollte . "tables" enthält danach tatsächlich eine Auflistung aller Tabellen der Datenbank. Die Verbindung steht also spätestens jetzt (warum nicht schon vorher entzieht sich meiner Kenntniss, da vorher bei ...
If Not result.Execute Then
Messagebox result.GetExtendedErrorMessage,,result.GetErrorMessage
Exit Sub
End If
... ja kein Fehler ausgegeben wird.
Nichts desdo trotz liefert
status = result.UpdateRow
wieder false zurück, und die Datenbank wird nicht aktualisiert.
Ist vielleicht ein blöder Gedanke, aber für mich sieht das so aus, als ob die Connection immer nur für das genau nächste Datenbankkommando gültig ist, und danach verworfen wird.
Also wenn da eine Messagebox aufginge, würde ich Euch nicht mit so schwammigen Vermutungen nerven, sondern einfach fragen warum keine Verbindung zustande kommt. Also es kommt definitiv eine Verbindung zustande, wie ich oben ja schon schrieb konnte ich die Tabellennamen ja bereits auslesen. Eine Glaskugel ist also nicht nötig. Ich habe mittlerweile auch noch ein paar von den Beispielen aus der Designer Hilfe ausprobiert, so Sachen wie Auslesen der Tabellenspalten, Anzeige von ein paar Feldwerten in einer Messagebox, usw. Alles klappt bestens.
Also zurück zur ursprünglichen Überschrift dieses Threads: "Probleme beim SCHREIBEN von Daten über ODBC in SQL Server"
Mein Problem ist dass ich nur weiß dass bei
status = result.UpdateRow
am Ende status = false ist. Warum, verrät mir weder Notes, noch der ODBC Treiber, noch das Fehlerlog des Datenbankservers. Mögliche Ursachen währen:
- Falsche Werte für die Felder beim INSERT, bzgl. Verletzung eines Contraints in der DB
Kann ich ausschliessen, denn bis auf eine Autowert ID-Spalte gibt es keine NOT NULL Felder, und die Testdaten aus meinem Testagent sind auch OK (Feldlängen, Datentypen usw.).
- Berechtigungsprobleme meines technischen Users
Kann ich auch ausschliessen, denn wenn ich mich mit dem technischen Account direkt am Microsoft Management Studio anmelde kann ich dort problemlos damit INSERT Statements absetzen.
So, Problem gelöst. Hier die Lösung:
In allen Beispielen zu ODBC Klassen in der Designer Hilfe sieht man, dass zunächst eine Query ausgeführt wird, wie ich meinte um unter Anderem dem ODBC Resultset zu sagen aus welchen Tabellen er lesen, und in welche er bei einem AddRow/UpdDate Row schreiben soll. So wie in meinem Beispiel:
qry.SQL = "SELECT * FROM ADDRESSRECORDS"
Während der bisher bei mir eingesetzte ODBC-Treiber für MySQL offensichtlich einen serverseitigen Cursor für das Recordset benutzt, verwendet der Treiber für den MS SQL Server einen clientseitigen Cursor, und lädt gemäß der Abfrage (select * from ...) erst einmal alle Daten nach Notes. In meinem Fall ist das eine Tabelle mit ca. 60 Spalten und ca. 200.000 Datensätzen. Das dauert natürlich eine Weile. Währenddessen wird aber der Code des Agenten weiter ausgeführt, und wenn schliesslich mit
status = result.UpdateRow
die neue Zeile in die Datenbank geschrieben werden soll, ist diese noch mit dem ersten Select beschäftigt, und die Verbindung blockiert. Das erklärt auch, warum ich zwar die Spaltennamen und ein paar erste Datensätze lesen konnte, nicht aber die Anzahl der Zeilen ausgeben konnte, denn das Einlesen der Daten war noch in vollem Gange.
Da ich ja eigentlich nichts aus der Datenbank lesen will, reicht es die Query so zu ändern:
qry.SQL = "SELECT * FROM ADDRESSRECORDS where 0 =1 "
Diese Abfrage ist schnell ausgeführt, und das Schreiben in die Datenbank funktioniert nun.
Danke an alle, die mir helfen wollten.