Blog Schröder

Sammlung von Codeschnipseln zu Programmierproblemen.
Daten aus fremden Quellen unterliegen deren Rechten.
Siehe auch: Disclaimer auf www.computer-schroeder.de

Dienstag, 29. September 2009

Fehler 8155: Keine Spalte wurde für Spalte x von DRVD_TBL angegeben

System: Access 2003 (ADP mit SQL Server 2005)

Dies ist ein Hinweis auf eine fehlerhafte SQL-Anweisung.
Bei mir entstand der Fehler, als die SQL-Anweisung zusammengestückelt wurde:

DoCmd.RunSQL "INSERT INTO myTAB (Wert) VALUES (" & txtFeld & ")"

Alles geht gut, wenn in txtFeld eine ganze Zahl steht. Steht aber eine Kommazahl (z.B. 0,5) drin, dann erscheint in der VALUES-Liste das  Komma, was bedeutet: es werden zwei Werte statt einem übergeben.
Das Komma wird mit folgender Schreibweise korrekt in den SQL-Dezimalpunkt umgewandelt:

DoCmd.RunSQL "INSERT INTO myTAB (Wert) VALUES (" & str(txtFeld) & ")"

Labels: , , , , ,

Dienstag, 11. August 2009

Web-Zugriff auf SQL Server

Der SQL Server 2005 erlaubt einer Web-Lösung (hier PHP) keinen Zugriff auf eine DB-Tabelle? Hier ein Beispiel für eine Fehlermeldung:

Warning: mssql_query() [function.mssql-query]: message: Die SELECT-Berechtigung wurde für das 'xxxx'-Objekt, 'yyyy'-Datenbank, 'dbo'-Schema verweigert. (severity 14) in C:\www\zzzz.php on line 26

Lösung:
Es gibt den speziellen Datenbankbenutzer *.IUSR_* (* für lokale Bezeichnungen). Diesen Benutzer muß man nicht selbst anlegen.
Über die Datenbankeigenschaften, Register Berechtigungen muß man ihm die Rechte für CONNECT, DELETE, INSERT, SELECT, UPDATE (soweit nötig) eintragen. Achtung, bei den Eigenschaften des Nutzers selbst ist das weder sichtbar noch möglich.

Hintergrund:
Der Nutzer IUSR - der bei Microsoft auch in anderen Kontexten existiert - ist speziell für anonyme Internetbenutzer gedacht, wie sie typischerweise bei Webbenutzung auftreten. Das heißt nicht, daß jeder Nutzer nun per DELETE einfach in der Datenbank irgendwas löschen kann, sondern nur, daß dies die Oberfläche darf, die der Nutzer benutzt. Nur wenn das PHP-(/ASP...)Programm von sich aus ein DELETE schickt, wird dies ausgeführt.
Beachten: die berühmte SQL-Injection (Es darf dem Benutzer nicht möglich sein, irgendwo einen SQL-Befehl einzutragen, der dann ungewollte Operationen ausführt - s. http://de.wikipedia.org/wiki/SQL-Injection).

Labels: , , , , , , , , , ,

Montag, 19. Januar 2009

Unterformular zeigt leeres Rechteck

Das Problem taucht in Accessprojekten (2002/2003) auf Basis SQL Server 2005 auf.
Die Datenherkunft darf in der ORDER BY-Klausel keine Bezeichnungen wie "XY.ID" enthalten. Erlaubt sind in dieser Klausel nur reine unqualifizierte Feldnamen wie "ID".

Hilfen:
Bearbeite die ORDER BY-Klausel für diesen Zweck in der SQL-Box, nicht per Abfrageassistent (er fügt den Tabellennamen bzw. -alias wieder zu).

Weise allen nicht eindeutigen Feldnamen, die in ORDER BY vorkommen, einen Alias zu und schreibe diesen hinter ORDER BY.

Notfalls eine Tabelle tabelle1 durch eine Unterabfrage ersetzen a la
...
FROM (Select ID as XYID, ... From tabelle1) AS XY
...

Labels: , , ,

Mittwoch, 13. August 2008

Verbindung eines ADP zur Laufzeit festlegen

Folgende VBA-Prozedur weist dem ADP eine neue Verbindung zu:

Public Sub OpenADPConnection(ByVal strUser As String, ByVal strPassword As String)
Const strCONNECTION_STRING As String ="Provider=SQLOLEDB;Data Source=deinServer;Initial Catalog=deineDatenbank"
CurrentProject.OpenConnection strCONNECTION_STRING, strUser, strPassword
If Not CurrentProject.IsConnected Then
MsgBox "Es konnte keine Verbindung hergestellt werden!"
End If
End Sub


Unangenehmer Effekt:
Access versucht, beim Start einer Anwendung die letzte Verbindung wiederaufzubauen und endet nach Wartezeit mit einem Fehler.
Verhinderung:
Man sollte seine ADP-Anwendung ohne Verbindungsdaten ausliefern und dann, wie oben beschrieben, die Verbindung per VBA beim Start der Anwendung aufbauen.
Die gespeicherten Verbindungsinformationen kann man aus der Anwendung entfernen, indem man per VBA die OpenConnection-Methode ohne Parameter aufruft.

Call CurrentProject.OpenConnection


Danach ist das ADP beim Start verbindungslos und es wird nicht mehr automatisch versucht, eine Verbindung aufzubauen.

Labels: , , , , ,

Samstag, 13. Oktober 2007

"????????" als Wert eines SQL Server-GUID-Feldes angezeigt

Problembeschreibung
Wenn Sie mit VBA-Code (VBA = Visual Basic für Applikationen) den Wert einer Jet-Replikations-ID oder eines SQL Server-GUID-Feldes (GUID = Globally Unique Identifier) abrufen und anzeigen, erhalten Sie den Wert "????????".
Ursache
Ein GUID ist ein 16-Byte-Datenfeld, das konvertiert werden muss, um von Microsoft Access interpretiert werden zu können.
Lösung
Konvertieren Sie das GUID-Feld mit der Funktion StringFromGUID() in eine Zeichenfolge, damit Access ein GUID-Feld richtig interpretieren und anzeigen kann. Wenn Sie z.B. den folgenden Code auf ein offenes Formular anwenden, in dessen Datensatzherkunft ein GUID-Feld enthalten ist, sehen Sie eine Zeichenfolge hexadezimaler Zahlen in geschweiften Klammern, denen das Wort "guid" vorangestellt ist, wiederum in geschweifte Klammern gesetzt.
Beachten Sie, dass
Debug.Print StringFromGUID(Forms![Formularname]![GUIDFeldname])
etwa so angezeigt wird:
{guid {3B9B63A3-863D-11CF-8CAE-00AA00C0016B}}
aus Microsoft Knowledge Base: 278099 (http://support.microsoft.com/kb/278099/en-us/)

Extraktion und Aufbereitung:

Function °°StringFromGUID(guid As Control) As String
Dim s As String
s = StringFromGUID(guid)
s = Right(s, Len(s) - 6)
°°StringFromGUID = Left(s, Len(s) - 1)
End Function

Labels: , , ,

Montag, 24. September 2007

Verbindung eines ADP zur Laufzeit festlegen

Eigentlich sollte man annehmen, dass man zu diesem Zweck einfach die Connection.Open-Methode der Currentproject.Connection (ADODB.Connection) verwenden kann. Dies trifft aber nur indirekt zu. Es funktioniert nicht, das Connection-Objekt direkt zu verwenden. Stattdessen muss man die OpenConnection- bzw. CloseConnection-Methode des CurrentProject-Objektes verwenden.

Den richtigen ConnectionString für die Verbindung zu ermitteln kann evtl. ein weiteres Problem darstellen. Denn wenn man sich den Connectionstring der CurrentProject.Connection anschaut, wird dort als Provider "Microsoft.Access.OLEDB.10.0" verwendet. Wenn man aber versucht, mit diesem Provider eine Verbindung zu öffnen, schlägt dies immer fehl. Stattdessen muss man auch für Access einen typischen OleDB-ConnectionString zum SQL-Server verwenden und dabei den Provider "SQLOLEDB.1" angeben, so wie man ihn aus der BaseConnection-Property des CurrentProject-Objektes auslesen kann. Es funktioniert übrigens nicht, dort einen Provider für ein anderes DBMS anzugeben.

Mit den obigen Informationen ausgestattet ist es eine Kleinigkeit, eine VBA-Prozedur zu schreiben, die dem ADP eine neue Verbindung zuweist.

Public Sub OpenADPConnection(ByVal strUser As String, ByVal strPassword As String)

Const strCONNECTION_STRING As String = _
"Provider=SQLOLEDB;Data Source=deinServer;Initial Catalog=deineDatenbank;"

CurrentProject.OpenConnection strCONNECTION_STRING, strUser, strPassword

If Not CurrentProject.IsConnected Then
MsgBox "Es konnte keine Verbindung hergestellt werden!"
End If

End Sub

Zuletzt noch ein Hinweis zu einem eng verwandten Problem. Meist möchte man ja die Verbindung eines ADP zur Laufzeit setzen, um eine fertige Anwendung in der Umgebung des Kunden zu deployen. Dabei tritt der unangenehme Effekt auf, dass Access beim Öffnen eines ADP erst mal versucht, die Verbindung zu dem Server und der Datenbank herzustellen, die zuletzt verwendet wurden. Da der Entwicklungsserver i.d.R. bei dem Wechsel von einer Entwicklungsumgebung zu dem Live-System aber nicht mehr direkt zur Verfügung steht, bleibt die Access-Anwendung dann beim Start so lange hängen, bis der Connection-Timeout abgelaufen ist und anschließend wird eine Fehlermeldung ausgegeben, dass der Server nicht erreichbar ist.

Um diesen unangenehmen Effekt zu verhindern, sollte man seine ADP-Anwendung ohne Verbindungsdaten ausliefern und dann, wie oben beschrieben, die Verbindung per VBA beim Start der Anwendung aufbauen. Die gespeicherten Verbindungsinformationen kann man aus der Anwendung entfernen, indem man per VBA die OpenConnection-Methode ohne Parameter aufruft.

Call CurrentProject.OpenConnection

Danach ist das ADP beim Start verbindungslos und es wird nicht mehr automatisch versucht, eine Verbindung aufzubauen.

(www.codekabinett.com)

Labels: , , ,

CurrentUser in Access Data Project (ADP)

In einer Access-MDB-Anwendung war es mit der Funktion CurrentUser() möglich, den angemeldeten Benutzer der Anwendung zu ermitteln. Der Aufruf von CurrentUser liefert in einem ADP aber immer "Admin" zurück, egal wer gerade angemeldet ist.

Die Erklärung dafür ist einfach. In einem ADP wird die Benutzerverwaltung von dem SQL-Server-Backend übernommen. Es gibt in Access tatsächlich nur noch den einen Benutzer "Admin". Wenn man nun wissen möchte, welcher Benutzer am SQL-Server angemeldet ist, muss man dazu auch den SQL-Server befragen.

Die T-SQL-Funktion SUSER_SNAME() liefert den Login des Benutzers am SQL-Server. Dieser Login kann sich durchaus vom Benutzernamen in der aktuellen Datenbank unterscheiden. Dieser Funktion kann die interne ID eines Logins auf dem SQL-Server als Parameter übergeben werden, um den UserName eines anderen Benutzers zu ermittelt. Für unseren Zweck reicht es aber aus, die SUSER_SNAME-Funnktion ohne Parameter aufzurufen. Also sieht das komplette SQL-Statement wie folgt aus:

SELECT SUSER_SNAME()

Wenn man nicht den Login-Namen auf den Server, sondern den Benutzernamen in der aktuellen SQL-Server-Datenbank benötigt, kann man analog zu dem obigen Beispiel die Funktion USER bzw. USER_NAME() verwenden.

Um aus Access heraus diese Information zu erhalten, muss man ein Recordset mit diesem SQL-Statement öffnen und den Wert dieses Feldes auslesen. Diesen Vorgang kann man komfortabel in eine eigene VBA-Funktion kapseln, die dann in dem Projekt die eingebaute Funktion CurrentUser ersetzt.

Hier ein Beispiel für eine solche Funktion:

Public Function AktuellerBenutzer() As String
On Error GoTo AktuellerBenutzer_Err

Dim rs As ADODB.Recordset

Const strSQL As String = "SELECT SUser_SName()"

Set rs = CurrentProject.Connection.Execute(strSQL)

AktuellerBenutzer = rs.Fields(0).Value

AktuellerBenutzer_Exit:
On Error Resume Next
rs.Close
Set rs = Nothing
Exit Function

AktuellerBenutzer_Err:
MsgBox Err.Number & " " & Err.Description, vbExclamation, "Error"
Resume AktuellerBenutzer_Exit
End Function


Die hier erwähnten SQL-Server-Funktionen enstammen dem Funktionsumfang des SQL-Servers 8.0 (SQL 2000).
(www.codekabinett.com)

Labels: , , ,

Datenbank 'xyz' wird schreibgeschützt geöffnet... bei ADP-Mehrbenutzerbetrieb

Wenn mehrere Benutzer die ADP-Datei öffnen, bekommen alle Benutzer nach dem ersten die Meldung 'Die Datenbank 'xyz' wird schreibgeschützt geöffnet...' Was kann man dagegen tun?

Das ist so 'by Design'. Bei einer MDB-Anwendung werden die Informationen, welcher Benutzer welche Objekte bearbeitet und damit sperrt, in der LDB-Datei gespeichert. Dadurch ist es möglich, dass mehrere Benutzer gleichzeitig Schreibzugriff auf ein und dieselbe Datei haben.

Bei einem ADP gibt es keine LDB-Datei, daher muss Access immer davon ausgehen, dass der erste Benutzer, der die Datei öffnet Änderungen an den Objektdefinitionen vornimmt und sperrt die gesamte Datei. Dadurch haben alle weiteren Benutzer nur noch schreibgeschützen Zugriff auf die Datei. Der Schreibschutz gilt nur für die ADP-Datei, d.h. Formulare, Berichte, Module, etc. aber nicht für die Daten auf dem SQL-Server.

Als einfachen Workaround für dieses Problem kann man Access im Runtime-Modus starten und die ADP-Datei öffnen. Im Runtime-Modus kann man sowieso keine Änderungen an den Objektdefinitionen vornehmen, daher unterbleibt in diesem Fall der Hinweis auf dem Schreibschutz.

Diesen Workaround kann man einfach realisieren, indem man die Anwendung über eine Verknüpfung startet und dort den Runtime-Switch in der Befehlszeile angibt. Die Befehlszeile einer Verknüpfung sieht dann etwa wie folgt aus:

"C:\Pfad\zu\MSACCESS.EXE" /runtime "C:\Pfad\zur\Anwendung.adp"

Dieser Workaround funktioniert aber nur mit maximal 20 geöffneten Instanzen der Anwendung. Wenn die Anwendung 21 mal oder mehr geöffnet wird, erscheint die Fehlermeldung 'Die ADP-Datei ist nicht im richtigen Microsoft Access Projektformat.' und es ist nicht mehr möglich weitere Instanzen zu öffnen.

Eine bessere Lösung für das Problem ist es, jedem Benutzer eine eigene Kopie der Datei auf seinem lokalen Rechner zur Verfügung zu stellen. Zu einem bringt dieses Vorgehen leichte Performancevorteile, da die Access-Objekte nicht über das Netzwerk geladen werden müssen. Außerdem ist dieser Ansatz stabiler, da jeder Benutzer nur in seiner eigenen Datei arbeitet und wenn diese beschädigt wird, keine anderen Benutzer beeinträchtig werden.

Um eine Anwendung in einem solchen Szenario regelmäßig updaten zu können, kann auf jedem Client der Start der Anwendung über ein Script oder ein Zusatzprogramm erfolgen, das ggfls. eine aktualisierte Version von einem Netzlaufwerk auf den lokalen Rechner kopiert. Diese Funktionalität kann man entweder selbst entwicklen, oder auf fertige Lösungen wie z.B. den Auto FE Updater von Tony Toews verwenden.
(www.codekabinett.com)

Labels: , , ,