Auto-Generierung von performantem Abfragecode
Geschrieben von: Christian Holm 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 FunktionDiese 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 FunktionDa 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 2Da 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: WriteAdovbsIncFunction WriteAdovbsInc() WriteAdovbsInc = "<" & "!-- #inc" & "lude file=""adovbs.inc"" -" & "->" End Function WriteMetadataFunction 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 ResultatWenn 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ßbemerkungDieser (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. Download des CodesKlicken Sie hier, um den Download zu starten. Verwandte Artikel
Benutzerverwaltung leicht gemacht: Teil 1 Wenn Sie jetzt Fragen haben...Wenn Sie Fragen rund um die in diesem Artikel vorgestellte Technologie haben, dann schauen Sie einfach bei uns in den Community Foren der deutschen .NET Community vorbei. Die Teilnehmer helfen Ihnen gerne, wenn Sie sich zur im Artikel vorgestellten Technologie weiterbilden möchten. Haben Sie Fragen die sich direkt auf den Inhalt des Artikels beziehen, dann schreiben Sie dem Autor! Unsere Autoren freuen sich über Feedback zu ihren Artikeln. Ein einfacher Klick auf die Autor kontaktieren Schaltfläche (weiter unten) und schon haben Sie ein für diesen Artikel personalisiertes Anfrageformular.
Und zu guter Letzt möchten wir Sie bitten, den Artikel zu bewerten. Damit helfen Sie uns, die Qualität der Artikel zu verbessern - und anderen Lesern bei der Auswahl der Artikel, die sie lesen sollten.
©2000-2006 AspHeute.com |