Garry Robinson
Thanks to Internet search engines, users are now starting to insist on tools that will interrogate the text in databases and retrieve information that relates to that text. This article outlines a simple tool that builds SQL statements that search any table in a "back-end" database. The sample application is geared for Access/Jet databases but could easily be modified for other databases.
One of the more powerful operating system utilities that comes with Windows today is the File Search Utility. We can search all files in a given directory tree that have a particular text string and, once we locate and open the desired file, might very well be able to continue searching using the applications own search utilityfor example, Word or Excels Search.
But what happens when the string is located in a database? If you can (you have rights to) open the database, you might be presented with an application, but you might not see anything unless you know what the tables names are, and so on. In this article, Ill show you how you can search all the tables in an Access database and then return which tables have the required string in them. The same techniques could be applied either directly to other databases or indirectly via Accesss linking feature. [If youre using SQL Server 7.0, be sure to find out whether the database has been indexed for full-text searches. You might also want to order part number 098-82314 or download Microsofts excellent white paper, "Textual Searches on Database Data Using Microsoft SQL Server 7.0," from www.microsoft.com/sql. Youll find out how you can use it to do proximity and natural language searches and even assign weights, and about the overhead and performance hit associated with it.Ed.]
Finding a text string
The concept behind this database search utility is that by using Microsoft Accessthe Jet Engineas a front-end database interface, database links can be set up to let you search just about any data source you want. Heres the program logic:
We create a front-end Access database with links to all the back-end tables we want a particular user or group of users to search.
We interrogate the back-end system tables to learn the table names.
Then we search each table to find out which columns contain text dataignoring the othersand construct a SQL statement to query all the text fields in each table.
We then run the query on each table using recordsets and find and report the first match so that the user knows which tables have the required string in them.
The code
All of the source code for this search tool lies under the search button. Initially, the software sets up the definitions of the variables and handles the form selections (see Figure 1). The first technical bit of the software is establishing the workspace and opening the Jet database that was selected by the user. We then loop through all the tables in the Table collection (avoiding the Jet system tables):
' Create Microsoft Jet Workspace object
Set wrkJet = CreateWorkspace(","admin", ",dbUseJet)
Set myDb = wrkJet.OpenDatabase(selectedFile, True)
' Loop through all tables extracting the names
For i = 0 To myDb.TableDefs.Count - 1
Set MyTable = myDb.TableDefs(i)
tableName = MyTable.Name
If Left(tableName, 4) <> "MSys" Then
Now the software opens a recordset and loops through all the fields in each table to establish which of the fields are actually text fields. At this stage, the filter that were going to use in the search of this table is reset:
numFields = MyTable.Fields.Count
Set rstSearchTable = myDb.OpenRecordset( _
tableName, dbOpenSnapshot)
wherestr = "
For j = 0 To numFields - 1
Set myField = MyTable.Fields(j)
fldStr = myField.Name
fldType = myField.Type
if fldType = dbText Then
Now the hard part. It doesnt take many database encounters before you realize how creative end users can be with their namesespecially given Accesss flexibility. Mostly youll find extended descriptions with spaces like "Emergency Contact First Name" or % or # or even full stops ("."). Access manages this internally by allowing you to wrap square brackets around the field or table name [ ]. The following code shows how to handle some of these:
blankpos = InStr(1, fldStr, " ") + _
InStr(1, fldStr, "#") + InStr(1, fldStr, "/")
If blankpos > 1 Then
fldStr = "[" & fldStr & "]"
End If
Now were going to assemble the SQL filter string for the text fields according to the entries that have been made for searching on the main screen. Because Im assuming that were searching an Access database, we use Jets LIKE in lieu of more standard SQL MATCHES. The wildcard character in Jet is an asterisk (*), while in ANSI SQL it can be either a percentage sign (%) or an underscore (_). Either way, the Jet engine sorts this out irrespective of what back-end database youre working on via ODBC. AndOrOpt is the option box that allows you to select whether to try to find both strings (if you use two) in the same field or simply find one or the other. SearchString is the field on the form where you enter the search string. Use the wildcard character in this field to search in any location of the field rather than invoking an exact match.
If andOrOpt(1) Then
andOrStr = " and "
Else
andOrStr = " or "
End If
If Len(wherestr) > 1 Then
wherestr = wherestr & " or "
End If
wherestr = wherestr & "(" & fldStr _
& " like '" & searchString(1) & "'"
If Len(searchString(2)) > 1 Then
wherestr = wherestr & andOrStr & fldStr _
& " like '" & searchString(2) & "')"
Else
wherestr = wherestr & ")"
End If
Now that weve built a suitable filter for the table, we start up the recordset that we established earlier and search for any success with the filter using the Recordset FindFirst method. At this point, the software writes the full SQL to the text box called sqlFilter and labels the search as successful or not. It doesnt continue on through the full table after matching the filter, as the aim of the software was to tell you where a string was in the database.
If Len(wherestr) > 1 Then
With rstSearchTable
.FindFirst wherestr
If .NoMatch Then
sqlFilter = sqlFilter & UCase(tableName) _
& " : Not Found" & vbCrLf & _
"Select * from " & tableName _
& " where " & wherestr & ";" _
& vbCrLf & vbCrLf
GoTo nextTable
End If
sqlFilter = sqlFilter & UCase(tableName) _
& " : FOUND" & vbCrLf & "Select * from " _
& tableName & " where " & wherestr & ";" _
& vbCrLf & vbCrLf
End With
End If
The software then continues on through all of the tables in the database, building a full list of those searches that either failed or were successful. To make the text output more readable, the text string thats sent to the sqlFilter text box is padded out using the vbCrLF constant that outputs carriage returns and linefeed.
Ideas for elaboration
The software demonstrated only determines whether a text string exists in a table, and then reports it. You could improve on this by running a query for each individual field in each table and reporting the fields that matched the query, changing the Access system table search routines to work on your own database structures by interrogating your database schemas to find which fields are text searchable, allowing for sequential searches (find next), and so on.
Download
VBSRCH.exeGarry Robinson runs a software development company called GR-FX based in Sydney, Australia. Several of his articles have appeared in Smart Access, and hes written a popular shareware data mining tool that allows you to drill down on data in any linked back-end database and then visualize that data in 2-D or 3-D using MS Chart. +61 2 9665 2871,
www.gr-fx.com, Garry@gr-fx.com.