Glengamoi (Forum) · AspHeute · .NET Heute (RSS-Suche) · AspxFiles (Wiki) · .NET Blogs

Auto-Generierung von performantem Abfragecode

Geschrieben von: Christian Holm
Kategorie: Datenbank

This printed page brought to you by AlphaSierraPapa

Dieser Artikel zeigt Ihnen wie man mit Hilfe eines server-seitigen Code Generators eine Datenbankabfrage erstellt, die einiges an Performance bietet. Der Code Generator ist dabei unabhängig von der Struktur der Datenbank. Den generierten Code können Sie danach als ASP-Datei abspeichern - und er ist sofort einsatzbereit.

Der Code Generator übernimmt lediglich zwei Werte - den Connection String und den für die Abfrage notwendigen Query String - die Arbeit erledigt der Generator. Das Interface ist von der Struktur her dasselbe wie aus dem Artikel Code Generator für die AddNew Methode. Deshalb will ich heute auch nur das Kernstück des ASP-Scripts - die einzelnen Functions des Generators - besprechen.

Als Testumgebung habe ich die MS Internet Information Services in der Version 5 und den MS SQL Server 2000 verwendet. Die Testdatenbank ist wieder einmal Northwind. Der Generator aber funktioniert mit Access oder anderen Datenbanken ebenso.

Um den ASP Code für die Datenbankabfrage zu erstellen besitzt der Generator mehrere VBScript Funktionen, die ich Ihnen im einzelnen nun vorstellen werde, und wir beginnen mit der Hauptfunktion: GenerateCode.

Die GenerateCode Funktion

Diese Funktion erledigt die Generierung des notwendigen ADO/ASP Codes. Zuerst werden die für die Abfrage erforderlichen Codezeilen in die Ausgabevariable strOutput geschrieben.

Function GenerateCode(ByVal strConn, ByVal strSQLStmt)
 Dim strOutput, objRS, fld, strFieldObjects
 strOutput =  WriteMetadata() & vbCrlf & "<" & "%" & vbCrlf
 strOutput = strOutput & "Set objRS = Server.CreateObject(""ADODB.Recordset"")" & vbCrlf
 strOutput  = strOutput & "strSQLStmt = """ & strSQLStmt & """" & vbCrlf
 strOutput  = strOutput & "strConn = """ & strConn & """" & vbCrlf
 strOutput  = strOutput & "objRS.CursorLocation = adUseClient" & vbCrlf
 strOutput  = strOutput & "objRS.CursorType = adOpenStatic" & vbCrlf
 strOutput  = strOutput & "objRS.LockType = adLockBatchOptimistic" & vbCrlf
 strOutput  = strOutput & "objRS.Open strSQLStmt, strConn" & vbCrlf
 strOutput  = strOutput & "Set objRS.ActiveConnection = Nothing" & vbCrlf

Der generierte Code beginnt mit der Funktion WriteMetadata, die uns, wie später genauer gezeigt, die ADO Konstanten mittels eines METADATA Statements einbindet. Danach geht es weiter mit der Initialisierung des Recordset Objekts - um die höchste Performance zu erhalten, lege ich ein client-seitiges (disconnected) Recordset an, das ich sofort nach Aufruf der Open Methode von der Datenquelle trennen kann (ActiveConnection = Nothing). Dadurch wird der SQL Server sofort entlastet, und ich kann so lange (oder langsam) mit den Daten weiterarbeiten, wie es mir Freude macht.

Im nächsten Abschnitt wird der Code erstellt, der für die Ausgabe der einzelnen Records verantwortlich ist. Dies geschieht aber nicht auf die herkömmliche Art und Weise. Denn ich erstelle eine Objektreferenz auf jede Spalte (repräsentiert durch ein Field Objekt), die es mir erlaubt, später ohne Zugriff auf die Fields Collection auszukommen. In einer While Schleife mit hunderten Records bringt diese Art der Programmierung einiges an Performance zusätzlich.

Set objRS = Server.CreateObject("ADODB.Recordset")
objRS.Open strSQLStmt, strConn
 
 strOutput = strOutput & vbCrlf & "If Not objRS.EOF Then" & vbCrLf
 For Each fld in objRS.Fields
   strFieldObjects = strFieldObjects & "  Set obj" & GenerateFieldname(fld.Name)
   strFieldObjects = strFieldObjects & " = objRS.Fields(""" & fld.Name & """)" & vbCrlf
 Next
 strOutput = strOutput & strFieldObjects
 strOutput = strOutput & "End If" & vbCrLf & vbCrlf

Die Generierung der Referenzen ist in einer If Bedingung geschachtelt - was ist, wenn keine Records zur Laufzeit geliefert werden? Wozu sollte ich Zeit verschwenden, Referenzen zu legen, die dann sowieso niemand verwendet. Für die Referenz benötige ich einen sinnvollen, eindeutigen Objektnamen. Diesen erstelle ich mit der GenerateFieldname Function, der ich den Namen der SQL Spalte aus dem Recordset übergebe. Sie ist verantwortlich, in VBScript Variablennamen nicht verträgliche Spaltennamen zu "korrigieren" - derzeit behandle ich nur das Leerzeichen.

Um auch die Verwendung der Referenzen zu verdeutlichen, ist in der abschließenden While Schleife ein auskommentiertes Beispiel mit dabei. Das sieht dann so aus:

 strOutput = strOutput & "While Not objRS.EOF" & vbCrlf
 strOutput = strOutput & "   ' SAMPLE CODE: Response.Write(objFieldname.Value)" & vbCrlf
 strOutput = strOutput & "   objRS.MoveNext" & vbCrlf
 strOutput = strOutput & "Wend" & vbCrlf & vbCrlf

 strOutput = strOutput & "Set objRS = Nothing" & vbCrlf

 strOutput = strOutput & "%" & ">" & vbCrlf
 GenerateCode = strOutput
End Function

Damit hätten wir die GenerateCode Funktion fertig durchbesprochen - jetzt zeige ich Ihnen noch, wie die in GenerateCode verwendeten Hilfsfunktionen Ihre Arbeit verrichten. Beginnen wir mit der GenerateFieldname Funktion.

Die GenerateFieldname Funktion

Da wie schon erwähnt der Fieldname manchmal etwas Kosmetik benötigt, da er Sonderzeichen, Leerzeichen etc. enthalten kann, erledige ich dies mit dieser Funktion. Allerdings Vorsicht - da wir keine Sonderzeichen verwenden, habe ich nur das auffälligste korrigiert, nämlich Leerzeichen - weil in der Northwind Datenbank solche vorkommen können.

Function GenerateFieldname(strSQLFieldName)
 Dim strFieldname
 strFieldname = Replace(strSQLFieldName, " ", "_")
 GenerateFieldname = strFieldname
End Function

ADO Konstanten einbinden - Mal 2

Da der generierte ASP Code ADO Konstanten verwendet, muß ich diese irgendwie definieren. Dies kann auf zwei Arten erfolgen: entweder ich inkludiere adovbs.inc, oder ich verwende ein METADATA Statement für die Type Library (der heute vorgestellte Code verwendet per Default die Typenbibliothek). Um es einfach austauschbar zu halten, habe ich die Einbindungen modular in Funktionen verpackt:

WriteAdovbsInc

Function WriteAdovbsInc()
 WriteAdovbsInc = "<" & "!-- #inc" & "lude file=""adovbs.inc"" -" & "->"
End Function

WriteMetadata

Function WriteMetadata()
  Dim strMD  
  strMD = "<" & "!--METADATA NAME=""Microsoft ActiveX Data Objects 2.5 Library"" "
  strMD = strMD & "TYPE=""TypeLib"" "
  strMD = strMD & "UUID=""{00000205-0000-0010-8000-00AA006D2EA4}""-->"
  WriteMetadata = strMD
End Function

Beide Funktionen erfüllen die Aufgabe auf gleiche Weise: sie setzen den benötigten Code aus einzelnen Fragmenten zusammen, sodaß der ASP Parser diese nicht mißinterpretiert.

Das Resultat

Wenn Sie nun den Code Generator (CodeGenSpeedyLoop.asp) auf einem Webserver ausführen, und Zugang zu einem Datenbank Server haben, erhalten Sie folgendes Resultat in einem Web Browser:

Abschließend habe ich noch, sozusagen als Funktionstest den vom Generator erstellten Code als ASP Datei abgespeichert und ebenfalls ablaufen lassen. Dabei habe ich mir den Firmennamen (CompanyName) aus der Shippers Tabelle anzeigen lassen:

<!--METADATA NAME="Microsoft ActiveX Data _
Objects 2.5 Library" TYPE="TypeLib" _
UUID="{00000205-0000-0010-8000-00AA006D2EA4}"-->

<%
Set objRS = Server.CreateObject("ADODB.Recordset")
strSQLStmt = "SELECT * FROM Suppliers"
strConn = "Provider=SQLOLEDB;server=bluescreen;uid=sa;pwd=;Initial Catalog=Northwind;"
objRS.CursorLocation = adUseClient
objRS.CursorType = adOpenStatic
objRS.LockType = adLockBatchOptimistic
objRS.Open strSQLStmt, strConn
Set objRS.ActiveConnection = Nothing

If Not objRS.EOF Then
  Set objSupplierID = objRS.Fields("SupplierID")
  Set objCompanyName = objRS.Fields("CompanyName")
  Set objContactName = objRS.Fields("ContactName")
  Set objContactTitle = objRS.Fields("ContactTitle")
  Set objAddress = objRS.Fields("Address")
  Set objCity = objRS.Fields("City")
  Set objRegion = objRS.Fields("Region")
  Set objPostalCode = objRS.Fields("PostalCode")
  Set objCountry = objRS.Fields("Country")
  Set objPhone = objRS.Fields("Phone")
  Set objFax = objRS.Fields("Fax")
  Set objHomePage = objRS.Fields("HomePage")
End If

While Not objRS.EOF
   Response.Write(objCompanyName.Value & "<br>")
   objRS.MoveNext
Wend

Set objRS = Nothing
%>

Schlußbemerkung

Dieser (weitere) Code Generator hilft wieder dabei, lästige Schreibarbeit bei umfangreichen Datenbankaufgaben abzunehmen. Da der Generator nur den Connection- und den Query Sting übernimmt, ist er für jede beliebige Datenbankstruktur anwendbar. Obendrein wird noch ein sehr leistungsfähiger Code produziert.

This printed page brought to you by AlphaSierraPapa

Download des Codes

Klicken Sie hier, um den Download zu starten.
http://www.aspheute.com/code/20010329.zip

Verwandte Artikel

Benutzerverwaltung leicht gemacht: Teil 1
http:/www.aspheute.com/artikel/20020429.htm
Code Generator für die AddNew Methode
http:/www.aspheute.com/artikel/20010327.htm
Der ADO Command Code Generator
http:/www.aspheute.com/artikel/20010308.htm
Die SQL2Table Komponente
http:/www.aspheute.com/artikel/20010522.htm
Intelligente Meta-Tags
http:/www.aspheute.com/artikel/20010620.htm
Optimiertes Erstellen von DropDowns
http:/www.aspheute.com/artikel/20040901.htm
Stored Procedures einfach erstellt
http:/www.aspheute.com/artikel/20020903.htm

 

©2000-2006 AspHeute.com
Alle Rechte vorbehalten. Der Inhalt dieser Seiten ist urheberrechtlich geschützt.
Eine Übernahme von Texten (auch nur auszugsweise) oder Graphiken bedarf unserer schriftlichen Zustimmung.