DataWright Information Services

Consulting and Resources for Excel and Access




Documenting query dependencies

If you have to document the queries on your database you will
often need to know:

  • What queries do I have in the database, and what is the SQL
    for those queries?
  • What queries and tables in the database lie upstream of any
    query; ie, what data does my query depend on?

In an earlier article I showed
how to document query SQL.
This article builds on that documentation to answer the second
question.

Query dependencies — built-in options

Access has a built-in option for viewing object dependencies, if
you have version 2003 or higher. It requires you to turn on a
feature called Name Auto-correct which has a reputation for causing
problems with forms, queries, reports and general database
performance, as well as contributing to database crashes (see
link). The
feature is enabled by default and the current recommendation is that
you turn it off. If you do decide to use Name Autocorrect, be aware
of the risks.

Viewing object dependencies in Access 2003

To view object dependencies in Access 2003, do the following:

  • Select an object (Table, Query, Form or Report) from the
    list on the left of the database container.
  • Select View | Object Dependencies. If the Name Autocorrect
    feature is turned off you will get a warning that the feature
    will be turned on, and that you may have a delay of several
    minutes. Click OK. You will see a new pane at the right of the
    database window, with all of the objects that depend on the one
    you selected. You can toggle the view to see objects on which
    your selected object depends.
  • If you select another object, go back to View | Object
    Dependencies and the pane will refresh.

Viewing object dependencies in Access 2007

  • Select an object from the list on the left of the database
    window.
  • In the Database Tools ribbon, select Object Dependencies
    from the Show/Hide group. You will get the same warning as from
    Access 2003 (above); click OK and the pane will appear at the
    right of the database window.
  • If you select another object, click Refresh on the pane to
    update the dependencies.

Turning off Name Autocorrect in Access 2000-2003

To turn off Name AutoCorrect do the following:

  • Go to Tools | Options | General
  • In the Name Autocorrect section (right hand side, halfway
    down), deselect the options

Turning off Name Autocorrect in Access 2007

In Access 2007, you turn off Name Autocorrect by doing the
following:

  • Click the Office button at the top left of the screen, and
    then select the Access Options at the bottom of the dialog
  • Select the Current Database button, and scroll to the bottom
    of the form. Deselect any options in the Name Autocorrect
    section and click OK. You may be prompted to restart the
    database before your changes take effect.

Query dependencies — do it yourself

To document your queries so that you can see the dependencies in
any version from 2000 up, you can add another couple of functions
and a new form to the original method.

First, you will need to modify the design of ztblQueries and
create a new table called ztblReferencedQueries, which holds the
relationship data. If you haven’t used
the code for documenting
tables
you will also need to create ztblTables and run the
WriteTableList function below.

The modified query table: ztblQueries

Field Data Type Comments
qryID AutoNumber Primary Key
QueryName Text (255)  
QuerySQL Memo  
QueryType Number  

The modified table has one additional field: a Number field
called QueryType.

A table for the relationship data: ztblReferencedQueries

Field Data Type Comments
RefID AutoNumber Primary Key
QueryName Text (255)
RefName Text (255)
Relationship Text (50)

A table that lists the tables in the database: ztblTables

Field Data Type Comments
TableName Text (255)  

The code

The first routine writes a list of tables — we’ll use this for
checking references to tables later on. Once existing records are
deleted, the function loops through the TableDefs collection, adding
the names of any tables that are not system, utility or temp tables.

Function WriteTableList()
  ''=========================================================''
  ''Writes name of all tables
  ''to ztblTables. Table is cleared first.
  ''=========================================================''
  Dim dbs As DAO.Database
  Dim rst As DAO.Recordset
  Dim tdf As DAO.TableDef
  Dim sSQL As String
  
  sSQL = "DELETE * FROM ztblTables"
  
  'Clear out ztblTables to avoid duplication
  DoCmd.SetWarnings False
  DoCmd.RunSQL sSQL
  DoCmd.SetWarnings True

  Set dbs = CurrentDb()
  Set rst = dbs.TableDefs("ztblTables").OpenRecordset

  'Add table names to ztblTables. The If statement excludes system tables (MSYS*), 
  'utility tables used in the documentation process (z*), and
  'temporary tables caused by deletion of tables in other routines (~*)
  For Each tdf In dbs.TableDefs 'exclude system, temp and accessory tables
    If Left(tdf.Name, 4) <> "MSYS" _
      And Left(tdf.Name, 1) <> "z" _
      And Left(tdf.Name, 1) <> "~" Then

      rst.AddNew
      rst!tableName = tdf.Name
      rst.Update
    End If
  Next tdf

  'cleanup
  rst.Close
  Set rst = Nothing
  Set tdf = Nothing
  Set dbs = Nothing
End Function

The second routine
(and the ztblQueries table) are documented in the previous article.
The code runs
through all queries, writing the query name, SQL and query type to
ztblQueries.

Once these two routines have run, the third routine (below)
populates ztblReferencedQueries.

Function ReferencesInQueries()
  ''=========================================================''
  ''Runs through query names in ztblQueries to see what tables
  ''and queries are referenced. Any that are found are written
  ''out to ztblReferencedQueries
  ''Written 11 Sep 2007 by Denis Wright
  ''Modified 12 Sep 2007 to account for parent and child references
  ''=========================================================''
  Dim dbs As DAO.Database
  Dim rstMain As DAO.Recordset
  Dim rstTable As DAO.Recordset
  Dim rstQuery As DAO.Recordset
  Dim x, y 'arrays to hold split data
  Dim sCheckParent As String, _
      sCheckChild As String
  Dim bChild As Boolean

  'define recordsets
  Set dbs = CurrentDb()
  Set rstMain = dbs.CreateQueryDef("", "SELECT * FROM ztblQueries").OpenRecordset
  Set rstTable = dbs.CreateQueryDef("", "SELECT TableName FROM ztblTables").OpenRecordset
  Set rstQuery = dbs.CreateQueryDef("", "SELECT QueryName FROM ztblQueries").OpenRecordset

  DoCmd.SetWarnings False
  'clean out existing data
  DoCmd.RunSQL "DELETE * FROM ztblReferencedQueries"

  'loop through all queries, checking against table and query names
  With rstMain
    .MoveFirst
    Do Until .EOF
      bChild = False
      If rstMain!QueryType = 80 Then 'Make-table query
        bChild = True
        x = Split(rstMain!QuerySQL, "FROM")
        y = Split(x(0), "INTO")
        sCheckChild = y(1)
        sCheckParent = y(0)
        ElseIf rstMain!QueryType = 64 Then 'Append query
          bChild = True
          x = Split(rstMain!QuerySQL, "SELECT")
          sCheckChild = x(0)
          sCheckParent = x(1)
          Else
            sCheckParent = rstMain!QuerySQL
      End If

      'loop through all table names
      With rstTable
        .MoveFirst
        Do Until .EOF
          If bChild Then
            Call AddRefRecord(sCheckChild, rstTable!tableName, _
             rstMain!QueryName, "Child")
          End If
            Call AddRefRecord(sCheckParent, rstTable!tableName, _
             rstMain!QueryName, "Parent")
        .MoveNext
        Loop
      End With

      'loop through all query names
      With rstQuery
        .MoveFirst
        Do Until .EOF
          If bChild Then
            Call AddRefRecord(sCheckChild, rstQuery!QueryName, _
             rstMain!QueryName, "Child")
          End If
            Call AddRefRecord(sCheckParent, rstQuery!QueryName, _
             rstMain!QueryName, "Parent")
        .MoveNext
        Loop
      End With
    .MoveNext
    Loop
  End With

  DoCmd.SetWarnings True

  'clean up references
  rstQuery.Close
  rstTable.Close
  rstMain.Close
  Set rstQuery = Nothing
  Set rstTable = Nothing
  Set rstMain = Nothing
  Set dbs = Nothing
End Function

To describe what the code is doing, let’s take a look at how it
deals with a make-table query. Assuming that we have a query called
qmakCustomers_AtoM, with this SQL:

SELECT Customers.*, Customers.CompanyName INTO Customers_AtoM
FROM Customers
WHERE (((Customers.CompanyName)<"N*"));

This part of the code splits the SQL statement into three
sections (Split takes an input string and a delimiter, and creates a
zero-based array of the substrings):

      If rstMain!QueryType = 80 Then 'Make-table query
        bChild = True
        x = Split(rstMain!QuerySQL, "FROM")
        y = Split(x(0), "INTO")
        sCheckChild = y(1)
        sCheckParent = y(0)

The first split results in the following 2 strings:

Array element String
x(0) SELECT Customers.*, Customers.CompanyName INTO
Customers_AtoM
x(1) Customers
WHERE (((Customers.CompanyName)<"N*"));

The first array element holds the two pieces of information that
we want from the SQL: the parent (source) and the child (dependent
table / query). By splitting x(0) again using INTO as the delimiter,
we end up with:

Array element String
y(0) SELECT Customers.*, Customers.CompanyName
y(1) Customers_AtoM

These two elements are then processed by the AddRefRecord
function, which creates a new record in ztblReferencedQueries for
each parent / child pair. The code for AddRefRecord is shown below.
The error handling creates the table if it doesn't already exist,
and the INSERT statement adds the new record.

Function AddRefRecord(sTestString As String, sCompareString As String, sQueryName As String, _
  sRelationship As String) As Boolean
  Dim sSQL As String

  'this SQL creates ztblReferencedQueries on the fly if it doesn't exist
  sSQL = "CREATE TABLE ztblReferencedQueries ( " _
    & "RefID COUNTER, " _
    & "ObjectName TEXT(225), " _
    & "RefName TEXT (255), " _
    & "Relationship TEXT(30)); "

  On Error GoTo Err_Handle

  AddRefRecord = False
  If InStr(1, sTestString, sCompareString) > 0 Then
    'add the new record
    DoCmd.SetWarnings False
    DoCmd.RunSQL "INSERT INTO ztblReferencedQueries ( ObjectName,RefName,Relationship,ObjectType ) " _
    & "VALUES ('" & sQueryName & "','" & sCompareString & "' _
    & ",'" & sRelationship & "','" & sObjectType & "')"
    DoCmd.SetWarnings True
    AddRefRecord = True
  End If
  
Err_Exit:
  Exit Function
Err_Handle:
  Select Case Err.Number
    Case 3192 'Table does not exist
    'create the table, then continue
    DoCmd.SetWarnings False
    DoCmd.RunSQL sSQL
    DoCmd.SetWarnings True
    Resume Next
  Case Else
    MsgBox "Error " & Err.Number & ": " & Err.Description
    Resume Err_Exit
  End Select
End Function