Search code examples
vbams-accessdatabase-schema

Update access backend schema with vba code


I have created an application in ms access with vba code. The app is split in front end and back end. Back end holds the user data. Now i want to add some new features but this needs some excessive back end changes in the schema structure.

My question is, what is the best way or practice to deliver to the end user the changes i want every time I upgrade my application? Of course it has to be something that keeps the current data intact.

I have seen some suggestions about a free program called CompareEm that compares the old and new database, and produces the appropriate vba code to do the job.

I was also considering if it would be more convenient to copy an empty database that has the wanted schema alongside the uograded frontend and having a module that compares the old database of the user with the empty one and try to change the old schema according the new one. (First my removing all relations, converting the tables, and then reapplying the new relations).

In any case i would like something that could be done automatically or by some customer code, in order to avoid messing up with users data.

Any help is appreciated.


Solution

  • This is one weak spot of Access. We don't have a really great scripting language and system built that allows scripting out of database "schema".

    So while the split design is a must have, and makes update of the code and application part very easy, a update of the back end (BE) is a challenge.

    I have done this in the past. And the way you go about this?

    Well, you have to make up a rule, and the rule is this:

    Anytime you add a new field/column, a new table?

    You MUST write the change(s) into a code module. In other words, you NEVER use the GUI anymore to add a new column (or say change the length). You code to that sub that will do this for you. In fact, in one application I had (many customers, all 100% Access runtime).

    So, to add new features, changes, fixes? I often had to add a few new columns or tables. So what I would do is WRITE the code into that "change/update" routine. (and I also passed it a version number). On startup, that code would be called.

    It started out with say about 5 lines of code. A few years later? I think it had well over 100 lines of code. And worked very well. If they opend a older data file (the BE), then if it really was a older verison - or say they had not paid for upgrades, then their current verison of software could and would be several upgrades behind. But, as noted, since on startup all of the "updates" to the database were ALWAYS written as code, then even if they were say 5 versions back, then all of the version numbered code would run, make the changes to the BE, and they would be up to date.

    So, this means you have to code this out. It not all that hard, but you DO HAVE do adopt this way of working.

    So, the code in that module looked like this:

    There were two common cases. The first case was a WHOLE new table. I did not want to write out the code to create that table, so what I would do is INCLUDE the whole new table in the FE for this purpose (and I would append the letter "C" to the table).

    So, the code on startup would check if the table exists, and if it does not, then I would execute a transfer command to copy the table. The code stub looked like this:

    ' check for new table tblRemindDefaults

    On Error GoTo reminddefaultsadd Set rst = CurrentDb.OpenRecordset("tblRemindDefaults")

    So, in above, I set the error handler and try to open the table. If the table does not exist, then above calls the routine remindDefaultsAdd. Its job of course is to add this new table to the BE.

    The above code stub looked like this:

    remindadd:
    
      Dim strFromDB        As String
      Dim strToDB          As String
      Dim strSql           As String
      Dim cp               As Object
      Dim shellpath        As String
    
      strFromDB = CurrentProject.FullName
      strToDB = strBackEnd
    
      DoCmd.TransferDatabase acExport, "Microsoft Access", strToDB, acTable, "tblGroupRemindC", "tblGroupRemind", True
    

    Note how then I simply copy a table from the FE to the BE.

    The next type of upgrade was adding a new column to a existing table.

    And again, similar to above, the code would check for the column, and if not present, I would add the column. In this case we not creating a new table, so no copy of the table.

    Typical code looked like this:

     ' add our new default user field
      
      Dim nF               As dao.field
      Dim nT               As dao.TableDef
      Dim nR               As dao.Relation
    
      strFromDB = CurrentProject.FullName
      strToDB = strBackEnd
    
      Set db = OpenDatabase(strToDB)
    
      Set nT = db.TableDefs("tblEmployee")
      nT.Fields.Append nT.CreateField("DefaultUser", dbText, 25)
      nT.Fields.Refresh
      Set nT = Nothing
    

    So in above we add a new field called DefaultUser to the table.

    If you OPEN DIRECT the BE, and do NOT use linked tables, then you are free and able to modify the table in question with code. You can ALSO use SQL ddl's statements. So while I noted that scripting support in Access is not all that great, you can do this:

    Set db = OpenDatabase(strToDB)
    
    strSql = "ALTER TABLE tblEmployee ADD COLUMN DefaultUser varCHAR(25);"
    
    CurrentDB.Execute strSQL, dbFailOnError.
    

    So, you can write out code using table defs, create the field and add it. Or you can use SQL DDL's statements and execute those. Of course each upgrade part I wrote had a version number test.

    So, the simple concept is that every time you need a new column, new table etc.?

    You write out this code to make the change. So, you might say work for a month or 2 add a whole bunch of new features - and some of these features will require new columns. So you NEVER use the GUI to do this. You write little code routines in your upgrade module, and the run them. Then keep on developing. So, when you done, all your updates to the table(s) are now done, and you can deploy the new FE to those users. On startup, the update code will run based on version number. (I have a small one row table in the FE with version number, and also a small one row table in the BE that has version number). After a upgrade, the new columns, new tables are now in the BE, and then of course I update that version number.

    So, you can use a combination of SQL ddl commands, or use code to create the field def as I did above.

    The only really important issue is that table changes can NOT be made against linked tables. So you have to create + open a SEPERATE instance of the database object as I did per above. And on startup, you have to ensure that no main form that is bound to a linked table has or is running yet. (since that will open the BE, and you can't make table changes against a open table).

    So, it not too hard to make the updates. But the HARD part is your self discipline . You have to ALWAYS go to your upgrade routines and add the new column in code. So that way after working for a few weeks, you have coded out the table changes, and thus are able to re-run that code against older existing BE's out in the field.