|
September 2001 |
Process SQL Scripts with BDE Local SQL
Steve Zimmelman
SQL scripts
have always been a useful tool for creating tables and indices, or just for
general data maintenance. Somehow, that feature escaped notice when Borland was
building its database engine. Steve Zimmelman explains how this small
component, TSQLScripts, might just fill the Local SQL scripts void for you.
The basic concept of processing scripts is this: Give the developer the
ability to place multiple SQL statements and comments in one file. Then execute
the statements in that file from top to bottom in a single process.
I designed my script engine to use the familiar double slash (//) for
comments, and a semicolon (;) for the statement delimiter. A simple example of
a script file might look like this:
// Create a table Create Table "MyTable.dbf" ( Name Char(30), Phone Char(13) ); // Create an index CREATE INDEX Name ON "MyTable.dbf" (Name) ;
The component merely parses each statement into a TStringList, and then
executes the statement via a TQuery object. The StringLists are managed using a
TList object.
Procedure TSQLScripts.ParseSQLScripts ; Var sl : TStringList ; i : Integer ; s : String ; NeedNew : Boolean ; Procedure NewScript ; Begin sl := TStringList.Create ; FList.Add(sl); End; Begin // Make sure all scripts are gone. FreeScripts ; // Add a new StringList for the first // SQL statement. NewScript ; // Parse each SQL statement into its // own StringList. For i := 0 To (FScripts.Count-1) Do Begin NeedNew := False ; s := Trim(FScripts[i]); // Ignore comments and blank lines. If (Copy(s,1,2)<>'//') And (s<>'') Then Begin If (s[1]<>';') Then Begin // Is the delimeter in this line? If (s[Length(s)]=';') Then Begin // Is there another SQL statement? If (i<(FScripts.Count-1)) Then NeedNew := True ; // Remove the delimeter Delete(s,Length(s),1); End; // Add the SQL statement its StringList sl.Add(s); End; End; // Is there another SQL statement? If (Length(s)>0) Then Begin If (Copy(s,1,2)<>'//') Then Begin If ((s[1]=';') Or (s[Length(s)]=';')) And (i<(FScripts.Count-1)) Then NeedNew := True ; End; End; If NeedNew Then NewScript ; End; // For (FScripts.Count-1) // Remove any empty StringLists. For i := (FList.Count-1) DownTo 0 Do Begin With TStringList(FList.Items[i]) Do Begin If (Text='') Or (Count=0) Then Begin Free ; FList.Delete(i); End; End; End; End;
The SQL statements are parsed from the script file, and then executed
sequentially in the same order they appear in the script file. The statement is
checked to see whether it requires a result set. If it does, the query is
opened using the Open method. If a result isn't required, the ExecSQL method is
used. The code that executes each SQL command looks something like this:
Var i : Integer ; sText : String ; ParseSQLScripts; For i := 0 To (FList.Count-1) Do Begin With FQuery Do Begin Close ; SQL.Clear ; // Get the SQL statement SQL.Text := TStringList(FList.Items[i]).Text ; // Does the statement require a result? If (UpperCase(Copy(SQL.Text,1,7))= 'SELECT ') Then Begin Open ; End Else Begin FQuery.ExecSQL ; End; End; End;
The component has a property called SQLScripts, a type of TStrings, which
contains the actual script. It also has a LoadFromFile method, so you can use
an external text file for scripts. This allows script execution to be more
dynamic in nature.
A simple usage of the component might look this:
SQLScripts1.LoadFromFile('MyScript.txt')
SQLScripts1.ProcessScript;
That's pretty much it. Using this technique, you can dynamically build entire data sets on the fly, or practically anything else you might do with Local SQL.