Mapping your data (1 Viewer)

mjdemaris

Working on it...
Local time
Today, 05:39
Joined
Jul 9, 2015
Messages
426
How do you approach mapping your data?

How do you approach tracking global/tempvars through the application?

I have been attempting to use Excel to track which variables change on events, and conditions, but it's getting a bit messy looking.

Thanks,
mjd
 

Ranman256

Well-known member
Local time
Today, 08:39
Joined
Apr 9, 2015
Messages
4,337
what do you mean map?
put data values in a state map, world map?
map changes to records?
 

The_Doc_Man

Immoderate Moderator
Staff member
Local time
Today, 07:39
Joined
Feb 28, 2001
Messages
27,172
You might have to search the forum for references, but there are such things as code analyzers. Some commercial products exist, but I rolled my own for the learning experience that it represented. Then the darned thing turned out to be useful. Before you ask, it was work done for hire for the U.S. Government, and I don't own it so can neither give nor sell it to you.

I'll tell you how these analyzers work. You HAVE to understand string parsing for at least some parts of this and should have a grasp of finite automata theory so you can avoid building a non-finite automaton.

In essence, you run discovery loops through every collection in your database - Tables, Queries, Forms, Reports, Modules. Don't know that macros CAN make a reference. These loops take the form of a "For Each <formal-object-name> in <collection>" ... "Next <formal-object-name>". Look up "VBA Collections" or "Component Object Model Collections" to see explanations of collection discovery.

For tables, you find one FieldDefs collection for each table and run a discovery loop to capture the name and other attributes of the fields. There is also the mSysRelationships table (a system table that is normally hidden, but you can safely read it) as a place where a field could be referenced. (Actually, TWO fields, one on each side of the relationship).

For queries, you find each QueryDef and look at its SQL, which will give you strings that can be parsed to find fields & tables. Both action queries and SELECT queries have a .SQL string property that should be parse-worthy.

For forms and reports, you find the .Recordsource, which will either be a named query or an actual SQL query. Again, parse. Then enumerate the controls. For those that have a .ControlSource you can remember the name. For those that have a .RowSource, more SQL to parse. Note that both forms and reports can have Sections which is itself a collection of segments that can contain collections of controls. Sections cannot, however, directly reference a field (I think) so the only thing to remember is in which section a given control was found. Don't forget that a form or report can have a Class Module (in which case the form/report .HasModule property would be True.)

I'll skip Macros because most of us don't use them, but I believe they involve text. So you MIGHT find queries with a RunSQL action or functions for a RunCode action.

The named general modules are sequences of text strings in the .Lines(n) collection. The same is true for the Class modules from forms/reports that are accessed through the .Module property of the form/report. Each string in a module has a line number relative to line 1 at the first line of the module. You can parse each string to see what it has in it. Line numbers can (& do) start over again for each module.

Why do all of this? You can do INSERT INTO for a table in which you store the name, where you found it, and whatever properties you can determine. You have to like parsing to make this work right, but it IS workable. When you are finished, you can write a report that steps through the object tables to show name, reference, attributes, etc.

A reference is either a DEFINING reference (FieldDefs in a TableDef or field alias-names in a QueryDef), DIM statements in class or general modules, formal arguments in SUB or FUNCTION statements, etc.; or a USING reference (fields in any query's SELECT list or other syntax (WHERE, ORDER BY, etc.), or in a form/report as a .ControlSource or in a .RecordSource or .RowSource SQL statement, or in a VBA statement that at least appears to be an expression containing a variable name.

I do not recommend the above approach for beginners, but if you choose to do this, when you are done you will know the structure of Access COM elements to a level of detail you really didn't think you ever wanted to know.
 

Galaxiom

Super Moderator
Staff member
Local time
Today, 22:39
Joined
Jan 20, 2009
Messages
12,852
For tables, you find one FieldDefs collection for each table and run a discovery loop to capture the name and other attributes of the fields.

There is a CurrentDb.TableDefs Collection but no FieldDefs Collection. There is a Fields Collection in a tabledef.
 

Galaxiom

Super Moderator
Staff member
Local time
Today, 22:39
Joined
Jan 20, 2009
Messages
12,852
How do you approach tracking global/tempvars through the application?

When I use them (very rarely), I put global variables in a separate module dedicated solely to their declaration. When used properly, there will be very few of them and no need for elaborate documentation to track them. Any documentation can be held as comments in this module.

Global variables are vastly overused by many developers who employ the sloppy technique of passing values internally between modules or procedures with them when they aught to be passing the values to procedure arguments or referring to module properties.

The fundamental programming principle of encapsulation does not involve having variables sitting around where they have excessive scope, making it difficult to keep track of what intentionally or inadvertently changes them. Procedure arguments are well defined and the procedure should never require values other than those passed to it as parameters or retrieved from tables.


TempVars are even more abused. Their scope covers the entire project and their intended purpose was to to allow variables to be used in queries. Some seriously misguided developers prefer to use them in code over global variables, simply because they don't get cleared in a project reset.

I have never used TempVars. Instead I pass a parameter to the querydef. Once again this is a far better programing practice because the parameter is a well defined property of the querydef. If a parameter is not passed each time the query is run, the runtime error raised is explicit. Using a TempVar, there is very real chance of failing to set its value, leaving the query to quietly run with a stale value.
 
Last edited:

The_Doc_Man

Immoderate Moderator
Staff member
Local time
Today, 07:39
Joined
Feb 28, 2001
Messages
27,172
There is a CurrentDb.TableDefs Collection but no FieldDefs Collection. There is a Fields Collection in a tabledef.

True that. I was a bit careless in the way I said it. Thanks for the clarification.
 

gemma-the-husky

Super Moderator
Staff member
Local time
Today, 13:39
Joined
Sep 12, 2006
Messages
15,652
I think whatever you do, keeping track of a big application is never going to be easy.

It's easier if you are the sole developer, as you can hold a view of the app in your head.
I don't use tempvars because I generally develop in A2003 which doesn't have them. I do tend to declare a lot of global variables, although I often put them inside record/type structures for convenience and for intellisense.

For security issues I restrict the use of global CONSTANTS very carefully.

I do declare and scope lots of local variables, but for things I want to persist beyond the life of a form I use public variables

I pass the variables to procedures, and very rarely reference the public variables directly, but it does still mean I need to note and/or remember their use so other procedures don't try and use/change the same variable at the same time.

In large apps I often find myself duplicating queries/variables/even functions - I know I have done them before but it's quicker to do them again rather than find the originals. (we are talking about apps with thousands of stored queries)

overall, I guess I am not too bothered about redundant code and variables.
If I want a new function, I'll might end up doing it from scratch, rather than try to find and repurpose existing code and functions. It might mean my dbs ends up a bit bigger than it need be.

I could use class modules, and I do for some things, but they aren't second nature to me.

I don't mean for one minute that my apps are badly designed/analysed. I think my data structures are as good as you will see anywhere. I just am not bothered about the code analysis - certainly not enough to try and analyse how all variables are used.


here's one I do use though. If I am looking for a field that I know is used in a query, then this finds all instances in moments. I have modified versions of this that searches for multiple strings, or selects just action queries rather than all queries. Optionally, it will save the results to a text file.


Code:
Sub search_queries_msgbox_quick()

Const searchfor = "unitprice"

Dim db As Database
Dim qdf As QueryDef
Dim s As String
Dim f As Long
Dim fname As String

s = ""

Set db = CurrentDb

For Each qdf In db.QueryDefs
            If InStr(qdf.SQL, searchfor) > 0 Then
                s = s & qdf.Name & vbCrLf
            End If
Next

If s = "" Then
    MsgBox ("Search String not found. ")
Else
    If MsgBox(s & vbCrLf & vbCrLf & "Output to File?", vbQuestion + vbYesNo) = vbNo Then
        Exit Sub
    Else
        f = freefile
        fname = CurrentProject.path & "\" & "log-" & Format(Date, "yyyy-mm-dd") & ".txt"
        Open fname For Output As #f
        Print #f, s
        Close #f
        FollowHyperlink fname
    End If
End If

End Sub
 

mjdemaris

Working on it...
Local time
Today, 05:39
Joined
Jul 9, 2015
Messages
426
@Dave

but for things I want to persist beyond the life of a form I use public variables

Where do you declare these public variables? If they are in the form's module, won't they be removed when the form closes? Otherwise, wouldn't they just be considered global variables?

I am also looking at using some class modules, though I am not quite sure how to go about it...reading up on that. Mainly for the user and the requisition form, kind of like an order form.

@Galaxiom
The best and most correct way to pass values is through the use of parameters/arguments. Ok, got it. I have been using globals so other form's procedures know what to do.

@Doc
My main concern was keeping track of variables' values at any given moment, but if I follow encapsulation, and restrict use of globals and tempvars, then I would not have to worry about that too much, except in the case of complex subs and functions within a module.

@Dave
I am trying to keep from reinventing the wheel as much as possible - for the experience (good programming practice) and to keep the db from getting over-sized.
 

gemma-the-husky

Super Moderator
Staff member
Local time
Today, 13:39
Joined
Sep 12, 2006
Messages
15,652
public/global is the same thing

public const = "MyAppName"

dim myvar as string 'local scope
public myvar as string 'global scope.

type TMyVars
somefield as string
anotherfield as long
end type

public MyVars as TMyVars


with the record type construct, when you type into the editor
myvars. you get intellisense.after the dot, which is useful.

It's always a way of grouping public variables you want to keep together. This helps prevent you inadvertently using a variable designed for a particular part of the application in a different part.

sort of part way to a class module, if you like - although you can only have one set of typed variables at a time. With a class module you can have more than one.
 

mjdemaris

Working on it...
Local time
Today, 05:39
Joined
Jul 9, 2015
Messages
426
Galaxiom:
Procedure arguments are well defined and the procedure should never require values other than those passed to it as parameters or retrieved from tables.

How would you pass multiple parameters to a form's opening events? It seems the Load event cannot accept argument, and I am not sure how I would pass several args through the OpenArgs parameter without creating a complicated bit of code to extract strings/integers from the OpenArgs.

I have successfully passed two "values", as string, then convert on the other end, but to add more...
 

gemma-the-husky

Super Moderator
Staff member
Local time
Today, 13:39
Joined
Sep 12, 2006
Messages
15,652
you can pass multiple openargs with a standard convention.

Say you use a pipe symbol, then just send

arg1 & "|" & arg2 & "|" & arg3

then split(openargs,"|")(1) gives you the second openarg. (its zero based)
 

mjdemaris

Working on it...
Local time
Today, 05:39
Joined
Jul 9, 2015
Messages
426
Back to mapping it all out:
What have you found to be your best practice at laying out the design of your application? As in using Excel, Word, or just on paper? Maybe I lose the big picture when I go home, or because I am doing so many different things at work, but I tend to create more forms, queries, variables, tables, variables than I end up needing in the end...so then I tend to lose track of which ones I am really using when I come back to the project a few days later.
 

NauticalGent

Ignore List Poster Boy
Local time
Today, 08:39
Joined
Apr 27, 2015
Messages
6,332
Dave, I was looking for something to just just what you code does. Great utility that I will be adding to my library. Thanks!
 

HiTechCoach

Well-known member
Local time
Today, 07:39
Joined
Mar 6, 2006
Messages
4,357
Back to mapping it all out:
What have you found to be your best practice at laying out the design of your application? As in using Excel, Word, or just on paper? Maybe I lose the big picture when I go home, or because I am doing so many different things at work, but I tend to create more forms, queries, variables, tables, variables than I end up needing in the end...so then I tend to lose track of which ones I am really using when I come back to the project a few days later.

I think part of the underlying issue has been revealed by this:
"but I tend to create more forms, queries, variables, tables, variables than I end up needing in the end...so then I tend to lose track of which ones I am really using when I come back to the project a few days later"

I hear this same thing form people who do all their development work in the live/production unsplit database.

I do the following:

Every database from the start is split into an App/front end end and data/back end.

The master front end version only contains fully tested objects used in the production release.

Use a development version of the front end and back end for testing new design ideas or making by changes.

I NEVER make design changes in the master front end. Only import objects from the fully tested development version.

I like to keep a journal of development activity in OneNote.

My thoughts on Splitting your Access database into application and data
 
Last edited:

Users who are viewing this thread

Top Bottom