Page 1

Click any image to view a larger version



First Published 8 Aug 2022             Last Updated 1 Sept 2022                 Difficulty level : Moderate


Section Links:
          Introduction
          Checking the MSysQueries Table
          The Lv Fields
          The Solution
          Downloads
          Future Plans



The first part of this article explained how Access stores query information using the MSysQueries system table

In this article, I will explain how Access retrieves the last saved view (design view/SQL view) information for future use



1.   Introduction                                                                                                                                         Return To Top

Almost all types of query can be created / edited in Design View or SQL View according to user preference.
However, Union / Data Definition / Passthrough query types are SQL-specific i.e. SQL view ONLY

When a query is opened, it will ALWAYS open in its last saved view – Design or SQL view.
BUT where does Access store this information for the next time of use?

One obvious place might appear to be the query default view property.
However, in recent Access versions (2013 onwards) , there is only one option – Datasheet view.

QueryDefaultView365
NOTE: Older versions of Access (up to 2010) had two other options – Pivot Table & Pivot Chart

QueryDefaultView2010
However, in all versions, the default view property refers to the view used when the query is run.

The last saved view used in query development must be stored somewhere else . . .
Over the years, I have never seen any explanation of how this is done.

However, I noticed something unexpected when I was researching the first part of this article . . .



2.   Checking the MSysQueries table                                                                                                       Return To Top

The MSysQueries table data is different depending on whether a SELECT query is opened in design view or SQL view.
For example:

        Design View
SelectInnerFiltDesign
        SQL View
SelectInnerFiltSQL



The SQL view is almost identical except it has 2 fewer records with the following items OMITTED:
     •   Attribute 1 – Flag = 1 (SELECT)
     •   Attribute 3 – Flag = 0 (No special attributes)

This pattern is repeated for other SELECT queries e.g. LEFT join

        Design View
SelectLeftDesign
        SQL View
SelectLeftSQL



However, if the SELECT query uses options such as DISTINCT / TOP / PERCENT / UNION then Attribute 3 ≠ 0.
In such cases, the MSysQueries data retains the Attribute 3 record in SQL view.

For example, a SELECT TOP PERCENT query:

        Design View
SelectTopDesign
        SQL View
SelectTopSQL



These differences seemed an unlikely explanation for the last saved view information.
Indeed it appeared to be rather strange behaviour at first sight.
However, I now think it does make sense.

To understand why, create a new query and go to SQL view without adding a table. The SQL window shows this:

SQLViewNoTable
Until now, I had always assumed this was done just to help users start writing the query. However, perhaps that’s not the case.

Access won’t let you save that empty query in SQL view so you can’t yet view the MSysQueries records.
However, you can go to design view and save the empty query. The query design window will of course be blank . . .

DesignViewNoTable
Notice the records for attributes 1 and 3

Stay in design view and alter the query property sheet as follows:
     •   Unique values = Yes
     •   Output All Fields = Yes
     •   Top Values = 5

The query design window will still be blank but the MSysQueries data has changed:

DesignViewNoTableOptions
In SQL view, we now see this:

SQLViewNoTableOptions
The values entered in the property sheet are of course shown in the SQL window but once again the record for Attribute 1 is missing.
In this case, Attribute 3 is shown as its Flag is non-zero.

So it would appear that Access omits the Attribute 1 Flag = 1 record as SELECT is added by default in SQL view.

Similarly it omits Attribute3 Flag = 0 as that Flag value indicates the default SQL and doesn’t need the SQL to be altered.

What about other types of query? APPEND / UPDATE / DELETE / MAKE TABLE

Attribute 1 will have Flag > 1 and a record for this will be shown.
Where Attribute 3 = 0, it is again omitted. However, a record does appear in cases where Attribute 3 ≠ 0

For example, consider a simple UPDATE query:

        Design View
UpdateDesign
        SQL View
UpdateSQL



Once again, the MSysQueries data is different when saved in each view.

However, for an action query which does include options such as Unique Records = Yes (DISTINCTROW), the MSysQueries records are IDENTICAL.
For example, this shows a DELETE DISTINCTROW query:

        Design View
DeleteDistinctRowDesign
        SQL View
DeleteDistinctRowSQL



Conclusion:
Access does NOT use the MSysQueries records to determine the last saved view.
I’ve checked the query definition properties and there are none which store this information.

Nevertheless, this information must be stored as part of the query itself



3.   The Lv Fields                                                                                                                                       Return To Top

The next items I investigated were the four Lv fields in the MSysObjects table. The fields are Lv, LvModule, LvExtra & LvProp

All 4 fields are OLE Object datatype.
Where the fields contain data, Access shows this as Long binary data
For queries, data is stored in the LvProp and LvExtra fields ONLY

MSysQueryObjects
It is possible to view this long binary data e.g. by exporting it to text files.

The screenshots below show the contents of the LvExtra and LvProp fields for a very simple SELECT query saved in design view and again in SQL view.

        Design View
LvDataDesign
        SQL View
LvDataSQL



Even for a simple SELECT query, the information is hard to read.
However, there do appear to be differences in the LvExtra field

More complex queries contain additional information in these fields and are even harder to decipher.

However, there is a much easier approach which does work . . .



4.   The Solution                                                                                                                                       Return To Top

We can use Application.SaveAsText to save the entire query to a text file in both design and SQL views

The text file output can be quite long but the differences between the two query views are obvious from the first line
For example, using the same simple SELECT query as above:

        Design View
qryCurrenciesDesign
        SQL View
qryCurrenciesSQL




In design view, the first 2 lines ALWAYS correspond to the Flag values for Attributes 1 and 3
     •   Operation = 1 corresponds to Attribute 1 Flag = 1 (SELECT)
     •   Option = 0 corresponds to Attribute 3 Flag = 0 (no special options)

The rest of the text file contains size & location info about the layout of each item in the query design window

In SQL view, the first item ALWAYS starts with dbMemo (long text datatype) followed by "SQL" then the query SQL (slightly modified for layout reasons)
The rest of the text file contains additional info about the layout of the SQL view window

Here’s another example. This time for a crosstab query:

        Design View
qryCrosstabDesign
        SQL View
qryCrosstabSQL



Conclusion
To get the last saved view we just need to read the first line of the query when output to a text file.
The screenshot shows a form listing all queries together with the query type, last saved view and last saved date information

FormQueryInfo
The form's record source is based on the following query:


SELECT MSysObjects.Name AS QueryName, tblSysObjectTypes.SubType AS QueryType,
      GetQueryLastSavedView([Name]) AS LastSavedView, GetDateLastUpdated([Name]) AS LastSavedDate
FROM MSysObjects INNER JOIN tblSysObjectTypes ON (MSysObjects.Flags = tblSysObjectTypes.Flags)
      AND (MSysObjects.Type = tblSysObjectTypes.Type)
WHERE (((MSysObjects.Flags)<>3) AND ((MSysObjects.Type)=5)) ORDER BY MSysObjects.Name;


The above query obtains this info as follows:

     •   QueryType – obtained from table tblSysObjectTypes based on the Flags value in MSysObjects

     •   LastSavedView – obtained using the GetQueryLastSavedView function
          This uses Application.SaveAsText to output the query to a text file (UTF-16 format)
          It is then converted to ANSI format so VBA can be used to read the first part of the file to identify it as design view or SQL view

          Code:

Function GetQueryLastSavedView(strQuery As String)
      'save the query as a text file (UTF-16 format)
      Application.SaveAsText acQuery, strQuery, CurrentProject.Path & "\qryUTF.txt"
      'convert the text file to ANSI format so it can be read using VBA
      UTF16toANSI CurrentProject.Path & "\qryUTF.txt", CurrentProject.Path & "\qryANSI.txt"

      'get the first 11 characters of the ANSI text file
      Select Case Left(ReadTextFile(CurrentProject.Path & "\qryANSI.txt"), 11)
      Case "Operation ="
            GetQueryLastSavedView = "Design"
      Case "dbMemo ""SQL"
            GetQueryLastSavedView = "SQL"
      Case Else
            'this shouldn't occur
            GetQueryLastSavedView = " "
      End Select

End Function


          The code for the UTF16toANSI procedure used above is in module modQueryInfo:

          Code:

Option Compare Database
Option Explicit
Private Const adReadAll = -1
Private Const adSaveCreateOverWrite = 2
Private Const adTypeBinary = 1
Private Const adTypeText = 2
Private Const adWriteChar = 0
Dim strText As String
Public strSQL As String
'=========================================
'Adapted from code at https://stackoverflow.com/questions/5182102/vb6-vbscript-change-file-encoding-to-ansi
Private Sub UTF16toANSI(ByVal UTF16FName, ByVal ANSIFName)

      With CreateObject("ADODB.Stream")

           .Open
           .Type = adTypeBinary
            .LoadFromFile UTF16FName
            .Type = adTypeText
            .Charset = "utf-16"
            strText = .ReadText(adReadAll)
            .Position = 0
            .SetEOS
            .Charset = "_autodetect"     'Use current ANSI codepage.
            .WriteText strText, adWriteChar
            .SaveToFile ANSIFName, adSaveCreateOverWrite
            .Close
      End With

End Sub


     •   LastSavedDate – obtained using the GetDateLastUpdated function:

Function GetDateLastUpdated(strQuery As String)

      'gets the last updated property as shown in the navigation pane
      GetDateLastUpdated = CurrentDb.QueryDefs(strQuery).Properties("LastUpdated")

End Function



As the data will change regularly over time, all the above field data is obtained at runtime
Obtaining all this info took a fraction of a second.

As an experiment, I ran the above query on a very large FE database for schools with 1582 queries.
It took about 12 seconds to open the query & move to the last record indicating the query had completed



5.   Downloads                                                                                                                                         Return To Top

Click to download:

This article as a PDF file:        MSysQueries Part2 SQL vs Design View

The example database:        MSysQueries Example - v2



6.   Future Plans                                                                                                                                      Return To Top

I partly investigated this topic just for my own interest.

However, I now hope to use the information in these two articles to display the SQL, design view and datasheet for a selected query on the same form.

The aim is to create something like this (similar to SQL Server Management Studio):

FormQueryViewer

UPDATE 01/09/2022
Building on the example app in this article, I have just uploaded a Query Metadata Viewer utility to the Example Databases section of this website

This was designed to display the metadata of each saved query and to view and edit the queries in SQL / Design & Datasheet views

QueryMetadata


I hope the information in these two articles was useful and may possibly encourage you to make use of the ideas discussed.

Please contact me using the form below with any feedback on this article including details of any errors or omissions.



Colin Riddington           Mendip Data Systems                 Last Updated 1 Sept 2022



Return to Access Articles Page Return to Top 1 2
Page 2 of 2