RustProof Labs: blogging for education (logo)
My book Mastering PostGIS and OpenStreetMap is available!

Building Reliable Data Solutions

By Ryan Lambert -- Published January 29, 2014

Warning: This post is outdated. It is here for reference purposes only.

Spreadsheet Hell. It's a place many people find themselves in and it's a difficult hole to dig yourself out of, because your spreadsheet is King of Spreadsheets and can do no wrong. Or, it does wrong frequently but you just can't seem to start the required Anarchy because Joe Executive might ask you a question only the King can answer. This post is quick summary about how I overthrow the King, one spreadsheet at a time and the tools I use to accomplish this feat. Reliable data solutions are a must for any business in this age.

Who Can Replace the King?

The initial requirements for the revolution to replace Spreadsheet Hell are as follows: Ability to replicate calculations in Excel, data entry, reporting, data integrity, and security. We want the data to be stored in SQL Server which allows me to ensure data integrity, security, and calculations. What we need now is something that can provide user friendly data input and flexible, functional reporting. Oh, and free is preferable in case you hadn't guessed it.

The Candidates

Unfortunately, the candidates to connect to our internal SQL Server are rather limited. We gave Perfectforms a good try but it had a shortcoming when it comes to printing (I wasn't directly involved with this issue, I have no more details, sorry). We built out another project using InfoPath which had a number of issues for me. The biggest problem was it's lack of integration with SQL, but apparently it works great if you need to connect to Sharepoint. For that project Sharepoint was not an option because we needed relational data and powerful querying abilities for writing the required reports.

There have been a few others that I/we have explored but the unfortunate candidate that keeps winning is.... MS Access.

Whaaaaa? You said you wanted Data Integrity!

You heard me right, MS Access has been the option that just won't die. Every time we explore a new possibility we run into issues that don't exist in Access. Such as... being able to easily connect to SQL Server. Or ability to easily create forms for data entry. Or to generate reports with charts.

Yes, it has its fair share of weaknesses which brings me to....

** Rule #1 of Well Crafted MS Access Front-ends:**

Give Access as few responsibilities as possible.

To illustrate my reasoning, here's an example from two weeks ago... I have been working on Database X to allow users at our 3 physical locations to administer a survey over the phone. I had the brilliant idea to save myself some time and allow a set of 5 forms to connect to a linked view (from SQL, of course) by setting the form fields to use the appropriate control sources. At first I was like "Woah, I forgot all this stuff just works"... and then it didn't.

Long story short:

... And then I scrapped the control source settings and went back to making the connections in VBA behind the scenes... and no more errors. My time wasted on this lousy experiment was minimal but it was a gentle reminder to not trust Access to do anything correctly.

Do Things Right

Before moving onto Rule #2, here's a laundry list of things I don't like about Access. (This is not an exhaustive list!)

Rule #2 of Well Crafted MS Access Front-ends:

Give SQL Server as many responsibilities as possible.

There are two basic ways the front-end should talk with SQL. It should be able to retrieve data relevant to the user, and send data back to SQL for storage. To accomplish these two functions I use views, stored procedures and user defined functions. In a recent post I wrote about how I created a custom function to use in views to help filter data based on the current user.

Stored procedures handle every single transaction that modified data. Period. Sure, it might seem like more work to start with because instead of writing a simple one-liner like:

INSERT INTO dbo.gradAttemptContact (gradID, employeeID) VALUES (1, 2);

You end up with a monstrosity such as:

CREATE PROC dbo.surveyAttemptContact  
    @gradID INT = NULL,
    @employeeID INT = NULL  
    AS  
    BEGIN  
        SET NOCOUNT ON;

        DECLARE @errMsg VARCHAR(500);  
        DECLARE @errLocation VARCHAR(50);  
        SET @errLocation = 'SP: surveyAttemptContact';

        BEGIN TRY  
            BEGIN TRANSACTION  
            INSERT INTO dbo.gradAttemptContact (gradID, employeeID)  
                VALUES (@gradID, @employeeID);  
            COMMIT
        END TRY  
        BEGIN CATCH  
            ROLLBACK  
            SET @errMsg = ERROR_MESSAGE();  
            EXEC dbo.errorLogInsert @location = @errLocation, @desc = @errMsg;  
            RETURN  
        END CATCH  
    END

This is the most extreme example I could find, but I believe the advantages far outweigh the extra bulk of code shown in the above example.

Let's just imagine that you have developed the perfect solution for Business Problem Q, so perfect in fact that there are no bugs in the code and the users LOVE using it. But then, the Business changes (gasp!) how they need to handle Problem Q, and your database isn't setup for that. If all of your logic is in Access either in defined queries or VBA you have to update the front-end and redistribute it to all the users. Bummer. OR.... if you have your logic for handling data in SQL you can make the necessary changes there and along you go. (This assumes the inputs/outputs don't change, of course)

Tightening Up Access

Now that we have our users getting data through pre-filtered views and sending data back through stored procedures, the only thing left to do is apply some security to keep them out of everything else. We use Active Directory with user groups setup so we only need to apply the permissions to the parent group(s). Here are a few examples of what you can do.

Allow the group to....

SELECT from a view:

GRANT SELECT ON [dbo].[vCurrentEmployee] TO [UserGroupHere]

UPDATE a view:

GRANT UPDATE ON [dbo].[vCurrentSurvey] TO [UserGroupHere]

Execute a Stored Procedure:

GRANT EXECUTE ON [dbo].[surveyStart] TO [UserGroupHere] AS [dbo];

Setting up access this way allows you to deny access to the base tables, such as dbo.employee, while allowing the users the ability to make the changes they need.

It's Not Perfect, but....

After 6 years of developing Access to be a front-end interface I have picked up a number of tricks, but only 2 rules. Ensuring data integrity and security is the most important thing to me and these tricks to control and restrict access have helped immensely.

How do you accomplish building similar solutions? Am I missing an incredible tool? Leave a comment and let me know!

By Ryan Lambert
Published January 29, 2014
Last Updated June 20, 2018