I have an Access database that contains worker information.
Some of this information is required by my coworkers to do their jobs, but some fields contain confidential information that they shouldn't have access to.
What I would like to do is split the database, with the back end encrypted and the front end compiled, so they can't change anything.
I don't have a problem with this aspect. The problem is that the front end that they would have access to would contain the entire table.
I want to know if there is a way to limit which fields are displayed in the front end database so my password protected back end would still contain everything, but front end users would only have access to the data I want them to. Any help/suggestions would be appreciated. Thank you.
Related
I am working on a split database in MS-Access 2010. The back end has relationships between many of the tables. My problem is the front end does not seem to recognise the relationships when I am designing forms, and the relationships do not show up in relationship view. For example, the field 'Service' in tbl_BAYGCharges is a lookup field pointing to the ID of tbl_BAYGServices, so tbl_BAYGServices.ID is related to tbl_BAYGCharges.Service in the back end. When I try to create a form for tbl_BAYGCharges that includes fields from tbl_BAYGServices, the tables do not show as related under 'Add Existing Fields' and a 'Specify Relationship' pop up appears.
I followed the instructions given on the MS website when splitting the tables and have tried re-linking all the tables which had no effect.
I'm relatively new to Access but I am assuming there is a problem here - I want to avoid having to manually create relationships every time I am creating a form and am worried about the future repercussions if the relationships in the back end aren't being recognised.
I'm turning to you for some help on an ACCESS FE/BE app.
I'm using VBA for quite some time now (Though only on ACCESS for like a year) but that's the first project where I actually need to setup a back-end on a distant server, and I'm struggling with what seems to be a connection issue (I'm no VBA Dev). I'm certainly doing things wrong, so feel free to redirect me on a good tutorial that could help me clear things up if that's the case.
So, my app is basically a storage database, we work on a file, add it to the BE through the FE with all relevant info, and users can look up things like client ID or name to get a list of all the files related and see where they are stored.
Everything works super well in local and is pretty much usable all-round with one user on a distant server, but things get complicated with multiple users. I have figured that the database seems to need to be entirely loaded in RAM to perform a search on its content, and that anytime someone modify a record, some flag tells Access to re-download the database on a new search to get everything fresh, which takes way too long (Around 30 seconds each time). Everything working as expected I guess, but I struggle to find a good way to have a multi-user app then. I can't imagine that's how it's supposed to work, so I guess I'm doing things the wrong way (Or it's the server, which is mainly a storage server for the entire company, that is not made for traffic purpose, but I dunno).
I have pinned the problem to this, which is a part of the code called when someone uses the search form :
Set searchRST = dbs.OpenRecordset(SQLQuery, dbOpenSnapshot)
If searchRST.RecordCount <> 0 Then
If searchRST.EOF = False Then
searchRST.MoveNext
End If
searchRST.MoveFirst
End If
Select Case searchRST.RecordCount
Case 0
.Controls("search_Results").Visible = False
.Controls("Button_Load").Visible = False
MsgBox ("No record found.")
Case 1
fileRST.FindFirst ("[ID] = " & searchRST![ID])
Forms(const_File).Bookmark = fileRST.Bookmark
.Controls("search_Results").Visible = False
.Controls("Button_Load").Visible = False
Call launchFileMode(modeVal)
Case Is > 1
searchRST.MoveLast
"Others checks are made before displaying results in a subform control
Basically, when you enter your search criteria and click on a button, it filters the database based on the SQLQuery string provided, goes through the entire filtered recordset so it can load it afterward, and either show a message " Nothing found " if the RecordCount of the filtered recorset is 0, show the only file available if it's 1 (By setting the bookmark of the recordset currently loaded by the File form to the bookmark of the filtered recordset), or show the filtered recordset in a subform if it's > 1 so the user can select the file to access.
The SQLQuery is a string programmatically created from the different searching criteria ( It was designed to handle a multiple criteria search but that's not used for now ), which gives something like this :
SQLQuery = "SELECT * FROM [DB] WHERE [CLIENT ID] LIKE '0123456';"
The loading time is not at the same point depending on the RecordCount :
If it's 0, the line Set searchRST = dbs.OpenRecordset(SQLQuery, dbOpenSnapshot) will get the charge ;
If it's 1, the line searchRST.MoveNext will get the charge ;
If it's > 1, the line searchRST.MoveLast ( Which is used to load the subform with all records of the filtered recordset so you can scroll down in it without delay ) will get the charge.
That's were I'm lost. Why isn't it the same line that get the loading time regardless of how big the recordset is ? If it's actually the action of retrieving the data that is problematic, it should be the dbs.OpenRecordset line that cause the problem in every situation, no ?
I'm probably doing things the worst way ever, so any advice on this or on any other way of handling these search would be greatly appreciated!
Feel free to ask for any detail that could be helpful.
(And sorry if it's not crystal clear, I'm working against the clock to post this from work where I can access the accdb file)
if there is only one record, then why a find first? If the query is similar to what you posted, then in most cases such a search for a ID will likely only return one record and should be fast. So most important is that the criteria limits the number of records pulled. Assuming some type of "key" is used, then as noted in the vast majority of cases you only likely will grab/return/pull one record from the server. You also need to 100% ensure that you have a persistent connection (a bound form remains open to a linked table that points to the back end.
It is also assumed that your application is split, and that each workstation has a copy of the FE.
It also assumed that the shared folder on the server for the back end is on your local network, and not some remote "VPN" type of connection over the internet which is usually about 100 times slower then your typical office network. You can read about the speed difference between a LAN (local area network) vs that of a WAN (wide area network) here:
http://www.kallal.ca//Wan/Wans.html
If you are working from a LAN, then I don't see why performance is slow, or any kind of issue (if it application runs fast with one user, but slow with two, then 9 out of 10 times, the persistent connection trick will fix this issue).
So basic assumptions are:
You application is split.
Each workstation gets a copy of the front end.
You have a persistent connection.
You are working on a standard office LAN as opposed to some kind of WAN (or VPN).
I want to have a "Login" form where a user would enter in a number that uniquely identifies them. After this number is entered I want all of the other forms to use that number on this local instance of the application. IE the user is "Logged In".
One solution would be to have a column in the list of users mark whether that user is the "Active" user, and have every form look up that field. This stops working when you want to have multiple people using the front end to access the same back end, as there would now be more than one "Active" user. Is there a way to make a global local variable that I can use to keep track of who is currently using this particular instance of the front end?
For reliable multi-user access each user must have their own local copy of the front-end regardless, so you could just save their ID number in a global variable or a local table in the front-end.
You can have a public function in a module that returns the ID (stored in the module) and call it in queries and form modules.
Note that variables are always stored per process in the RAM and are therefore always per-user.
I have removed the lookup information from a back-end Access table.
When I open the front-end mdb and try to view the raw data in the table, I am presented with an 'Enter Parameter Values' dialog box, and Access is indicating that it wants a value for 'Lookup_[ForeignKeyColumn].Desc'.
I can view the design of the table in the front end, and on the lookup tab, all the fields are set to 'Text Box', so it doesn't look like any references are floating around.
Queries are still fine - I can access the data - just looking at the table on its own is the difficulty.
Has anyone seen this before and knows what to do?
The solution: Refresh Linked Tables
From the front end, I had to go into Tools -> Database Utilities -> Linked Table Manager
and nominate to update all files.
Access has a lot of "ghost" issues. I've been working with it for 20 years, and I can't tell you how many times I've had to rebuild databases because mystery queries were running in the background, or forms continued to require data entry, even though the controls had been deleted.
Try creating a new, fresh database with nothing in it. Then, import all of the forms, queries, modules and macros into it. See if that fixes the issue.
I faced the same issue, however for me refreshing the linked tables did not work, neither did relinking the tables after first breaking the links.
In my case the field "Order By" in the property sheet of the front end table was filled. Clearing this resolved the issue.
Considering the set up:
Ms Access Application split into Front End and Back End = both native MS Access
Front End consists of forms only - it will be the only way to access data
Front End copy distributed to each user machine (thanks for answers to this question)
I need to implement the following scenario:
Ms Access application with < 20 users,
each user is assigned to 1 to 10 projects,
when user opens the MS Access he should only be presented with the data related to the project(s) he is assigned to
So for example we have users:
John
Owen
John is assigned to projects A,B,D.
Owen to B,D
When John logs in he can see only data
related to projects A,B,D. When Owen
logs in he can see only B,D
John and Owen can access the application at the same time
Related tables in the Back End
user
project
userProject - links user(s) to project(s) in many to many relationship.Each user can be assigned to one or more projects, one or more users can work on a project.
I came across this solution on databasedev.co.uk which basically uses a hidden form to store the current users details and then using this to filter the data on other forms.
So here is my Question:
Would that be the recommended solution? Are there any better options? I was thinking that I could use a table on the Front End instead of the hidden form for example.
Edit Re Comment
I see no reason why you should not maintain a table of users in the back-end with a join table of user, project that can be used to filter the projects.
The current user can be obtained with code if you are using network name (http://www.mvps.org/access/api/api0008.htm), it can be stored to a hidden field on the form, which would be useful for setting the form to relevant projects, or you could store the name to a custom database property (http://wiki.lessthandot.com/index.php/Custom_Database_Properties_Creation_and_Use)
The code below applies to finding number of logged-in users.
You could use provider specific ADO Schemas. You need to pass a valid connection, for example:
ADOUserList Currentproject.Connection
Public Sub ADOUserList(oConn As ADODB.Connection)
Dim rs As ADODB.Recordset
Set rs = oConn.OpenSchema(adSchemaProviderSpecific, , "{947bb102-5d43-11d1-bdbf-00c04fb92675}")
Debug.Print rs.GetString
rs.Close
End Sub
More information: http://msdn.microsoft.com/en-us/library/aa155436.aspx
I have a similar, if slightly more complex, situation. In my case, Users are assigned to User Groups, which have varying permissions over Access objects (forms, reports etc). They also have Projects to which they are assigned, and Preferences. Tables:
user
user_group
user_pref
project
user_project
I still, however, use a hidden form (session) which holds session information about the user that's currently logged in. eg: user_id, user_name, subform of assigned projects, preferences (such as 'Current Project').
Finally, a module basSession contains all the functions I need to get or set any of the session information in the hidden form, eg gfSession_GetUserID().
HTH
Be aware that in your current setup there is no way a method to 'Identify users and based on that limit access to data'. If all data resides in a shared backend Access file, your users can just open the back-end database and browse through all data. The only way to actually limit your users' access to the data is by using a database server.
If you want to go to Access I would suggest that you create queries for all (important) tables and use these queries in the forms. Include a WHERE statement in the query that limits the output to what the user may view. You can do this by either changing the complete SQL on opening of the database or include a global variable in the WHERE part of the query and set that variable to the current UserID.