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: , , , , , , , , , ,

Dienstag, 23. Juni 2009

In Datenherkunft von Formularen und Berichten Textteile austauschen

Bei Änderungen an der Datenbankstruktur sollen auch Tabellen- und Feldnamen geändert werden. In Modulen geht das einfach durch STRG + H (Ersetzen). Die in Access-Objekten eingebetteten SQL-Anweisungen werden so nicht erwischt. Hierzu folgende Funktion, die zweckmäßigerweise aus einem Makro (s. Abb.) aufgerufen werden kann.

Argumente:

  1. oldString: das alte Textstück (z.B. "Länderschlüssel")
  2. newString: das stattdessen einzusetzende (z.B. "Laenderschluessel")
  3. findInForms: Ersetze in RecordSource jedes Formulars und Berichts (nur FALSE, falls nicht gewünscht)
  4. findInControls: Ersetze in RowSource aller Listen- und Kombinationsfelder (nur FALSE, falls nicht gewünscht)



Public Function RenamePartInRSources(oldString As String, newString As String, _
Optional findInForms As Boolean = True, Optional findInControls As Boolean = True)
'ersetzt in der Datenherkunft von Form/Control ein Wort durch ein anderes
On Error GoTo Er
Debug.Print "Ersetze " & oldString & " durch " & newString
If oldString = "" Then Err.Raise 10001, , "Parameterfehler oldString"
If newString = "" Then Err.Raise 10002, , "Parameterfehler newString"
If Not (findInForms Or findInControls) Then Err.Raise 10003, , "Parameterfehler findIn*"

Dim AO As AccessObject
Dim F As Form
Dim R As Report

Dim o As String
Dim s As String
Dim i As Integer

For Each AO In Application.CurrentProject.AllForms
DoCmd.OpenForm AO.Name, acDesign, , , acFormEdit, acHidden
Set F = Forms(AO.Name)
If findInForms Then 'in Formular ersetzen
o = F.RecordSource
' Debug.Print F.Name & ": " & F.RecordSource
s = Replace(o, oldString, newString)
F.RecordSource = s
If s <> o Then Debug.Print F.Name & ": " & F.RecordSource
s = ""
End If

If findInControls Then 'in Steuerelement ersetzen
For i = 0 To F.Controls.Count - 1
On Error Resume Next
o = F(i).RowSource
' Debug.Print F.Name & "." & F(i).Name & ": " & F(i).RowSource
s = Replace(o, oldString, newString)
F(i).RowSource = s
If s <> o Then Debug.Print F.Name & "." & F(i).Name & ": " & F(i).RowSource
On Error GoTo Er
s = ""
Next i
End If
DoCmd.Close acForm, AO.Name, acSaveYes
Next AO

For Each AO In Application.CurrentProject.AllReports
DoCmd.OpenReport AO.Name, acDesign, , , acHidden
Set R = Reports(AO.Name)
If findInForms Then 'in Bericht ersetzen
o = R.RecordSource
s = Replace(o, oldString, newString)
R.RecordSource = s
If s <> o Then Debug.Print R.Name & ": " & R.RecordSource
s = ""
End If

If findInControls Then 'in Steuerelement ersetzen
For i = 0 To R.Controls.Count - 1
On Error Resume Next
o = R(i).RowSource
s = Replace(o, oldString, newString)
R(i).RowSource = s
If s <> o Then Debug.Print R.Name & "." & R(i).Name & ": " & R(i).RowSource
On Error GoTo Er
s = ""
Next i
End If
DoCmd.Close acReport, AO.Name, acSaveYes
Next AO
Ex:
Set AO = Nothing
Set F = Nothing
Set R = Nothing
Debug.Print "RenamePartInRSources beendet."
Exit Function
Er: MsgBox "Fehler " & Err.Number & " in Tools.RenamePartInRowSource" _
& vbCrLf & Err.Description
Resume Ex
End Function

Labels: , , , , , , , ,

Sonntag, 28. Oktober 2007

multipart identifier ... could not be bound

Dies bezieht sich darauf, wenn ein Accessprojekt (ADP) auf einen SQL Server greift, den es  noch nicht kennt (z.B. Access 2003 auf SQL Server 2005).

Mit Multipart identifier ist sowas gemeint wie TabAlias.Feldname (A.ID oder so).
Der Fehler kommt aus dem SQL Server und folgende gefundene Hinweis lauten:
" Oh, yes - is there an IPCode yolumn in the dbo.ClassifiedAd table?
If so, then the optimizer might have processed the explicit join (... JOIN
ON ...) before the implicit one (FROM dbo.ClassifiedAd Ad, dbo.Objects O ...
WHERE ...)"
"Why are you mixing join types (old-style vs. ANSI)? Why do you not use the
alias prefixes on all of your columns? I'll try to re-write this so the
parser understands it, but I have no idea what your table structure looks
like, so I can't fix all the prefixes."
(s.http://www.dbtalk.net/microsoft-public-sqlserver-programming/multipart-identifier-304262.html)

Geholfen hat mir: Ohne Wizzards in der SQL-Anweisung die Tabellenaliase (auch vor den Feldnamen) zu entfernen. Im betreffenden Fall jedenfalls war das möglich ;-)

Nachtrag:
Es genügt, wenn in der ORDER BY- Klausel keine Tabellenqualifizierer stehen (ggf. Spaltenaliase verwenden)

Labels: , ,

Donnerstag, 16. August 2007

Zeile in DB-Tabelle zufügen

''' <summary>
''' fügt einer Tabelle eine neue Zeile zu und trägt die ID ein
''' </summary>
''' <param name="table">Die Tabelle</param>
''' <param name="ID">Wert des Schlüsselfeldes ID</param>
''' <param name="values">Datenwerte je nach Tabelle in der Feldreihenfolge der Tabelle</param>
''' <remarks>Dies ist nur möglich für Tabellen mit dem Feldnamen ID</remarks>
Public Sub DBInsert(ByVal tablename As String, ByVal ID As String, ByVal ParamArray values() As Object)

'baue die Werteliste und erkenne, wie viele Werte hineingehören
Dim valueList As New StringBuilder
For i As Integer = 0 To UBound(values, 1)
valueList.Append("'" & values(i) & "', ")
Next i
valueList.Remove(valueList.Length - 1, 2) 'schneide letztes Komma ab

'Erzeuge die Abfrage (Einfügen: ID, Werte, Pseudo-_TimeStamp)
Dim sqlText As String = String.Format("INSERT INTO [{0}] VALUES ('{1}', {2})", tableName, ID, values.ToString)
Dim acceptedRows As Integer
Try
acceptedRows = doSql(sqlText, QueryReturns.Value) 'Einfügen der Zeile
Catch ex As Exception
f = Format("Einfügen in Tabelle {0} fehlgeschlagen" & vbCrLf, tableName)
MsgBox(sqlText & vbCrLf & ex.Message)
Exit Sub
End Try
If acceptedRows = 0 Then
f = String.Format("Es konnte keine Zeile in Tabelle {0} eingefügt werden." & vbCrLf, tableName)
MsgBox( f & String.Format("Nicht eingefügte Werte für Key {0}: {1}", ID, values.ToString))
Exit Sub
End If
End Sub


--- alt ---

''' <summary>
''' fügt einer Tabelle eine neue Zeile zu und trägt die ID ein
''' </summary>
''' <param name="table">Die Tabelle</param>

''' <param name="ID">Wert des Schlüsselfeldes ID</param>
''' <param name="value1">und folgende: Datenwerte
''' je nach Tabelle in der Reihenfolge der Tabelle.
''' Für NULL-Wert Argument auslassen (nur Komma schreiben)</param>

''' <remarks>Dies ist nur möglich für Tabellen mit dem Feldnamen ID.</remarks>

Public Sub DBAddRow(ByVal table As TMserverDBTables, ByVal ID As String, _
Optional ByVal value1 As Object = Nothing, Optional ByVal value2 As Object = Nothing, _
Optional ByVal value3 As Object = Nothing, Optional ByVal value4 As Object = Nothing, _
Optional ByVal value5 As Object = Nothing, Optional ByVal value6 As Object = Nothing, _
Optional ByVal value7 As Object = Nothing, Optional ByVal value8 As Object = Nothing)

Dim f As String 'Hilfsstring
'Schließe die falschen Tabellen aus
Dim tableName As String = Enum.GetName(GetType(TMserverDBTables), table)
'erlaubte Tabelle?
If (table And 1) Or (table And 2) Then
f = "Tabelle " & tableName & " nicht als Argument zugelassen"
log.Fault("DBAddRow", f)
Exit Sub
End If

'baue die Werteliste
Dim values As New StringBuilder
values.Append(IIf(value1 Is Nothing, " NULL", " '" & value1 & "'"))
values.Append(IIf(value2 Is Nothing, ", NULL", ", '" & value2 & "'"))
values.Append(IIf(value3 Is Nothing, ", NULL", ", '" & value3 & "'"))
values.Append(IIf(value4 Is Nothing, ", NULL", ", '" & value4 & "'"))
values.Append(IIf(value5 Is Nothing, ", NULL", ", '" & value5 & "'"))
values.Append(IIf(value6 Is Nothing, ", NULL", ", '" & value6 & "'"))
values.Append(IIf(value7 Is Nothing, ", NULL", ", '" & value7 & "'"))
values.Append(IIf(value8 Is Nothing, ", NULL", ", '" & value8 & "'"))
'Erzeuge die Abfrage
Dim sqlText As String = "INSERT INTO '" & tableName & "' VALUES ('" & ID & "', " & values.ToString & ")"

Dim acceptedRows As Integer
Try
acceptedRows = doSql(sqlText, QueryReturns.Value) 'Einfügen der Zeile
Catch ex As Exception
f = Format("Einfügen in Tabelle {0} fehlgeschlagen", tableName) & vbCrLf
log.Fault("DBAddRow", f &amp; "ID: " & ID & " Values: " & values.ToString & vbCrLf & ex.Message)
Exit Sub
End Try
If acceptedRows = 0 Then
f = "Es konnte keine Zeile in Tabelle " &amp; tableName & " eingefügt werden." & vbCrLf
log.Fault("DBAddRow", f & String.Format("Nicht eingefügte Werte für Key {0}: {1}", ID, values))
End If
'fertig
log.Test("DBAddRow", String.Format("Eingefügte Werte für Key {0}: {1}", ID, values))
End Sub

Labels: ,