Access - VBA Sub not called [closed] - ms-access

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 8 years ago.
Improve this question
I am required to provide a way for users to choose which column they want to display from a table. The user has a combo box of column names in a form which they can select one of and then hit a button which opens a report. I wrote some code which creates an SQL query based on the user's input, but it's not being called. I've tried inserting MsgBox statements, asserts, etc., but it never gets called. If I use a macro instead, that will be run, but not this sub...
Private Sub Report_Open(Cancel As Integer)
Dim field As String
Dim sqlQuery As String
Dim ctl As Control
ctl = "Forms![RuleGroupForm]![selectRuleFieldCombo]"
field = "rules.[" & ctl & "]"
' E.g. if the field was "Source File", the final query should look like this:
'SELECT overview.ID, relationship.rulesID, rules.[Source File], overview.[Rule Group], rules.RulegroupID
'FROM (overview INNER JOIN relationship ON overview.id=relationship.id)
'INNER JOIN rules ON relationship.rulesID=rules.ID
'WHERE rules.[Source File] IS NOT NULL;
sqlQuery = "SELECT overview.ID, relationship.rulesID, " & field
sqlQuery = sqlQuery & ", overview.[Rule Group], rules.RulegroupID"
sqlQuery = sqlQuery & "FROM (overview INNER JOIN relationship ON overview.id=relationship.id) "
sqlQuery = sqlQuery & "INNER JOIN rules ON relationship.rulesID=rules.ID"
sqlQuery = sqlQuery & "WHERE " & field & " IS NOT NULL;"
Me.RecordSource = sqlQuery
Requery
debugText = "New SQL Query = " & sqlQuery
End Sub
Thanks.
Edit: Resolved, not sure how to respond to own question...

There are a few things to check, apart from close and open, when you get odd behaviour such as this in Access. One is to see if the event on the properties sheet for the form or report, Open in this case, still contains [Event Procedure] - occasionally a form or report can have the code, but the event is not marked. When developing, that is, adding forms, reports and code, it is a good idea to run Compact and Repair on a regular basis, this also means regular back-ups - a simple copy saves a lot of grief. Finally, when developing fairly regular Decompiles are a good idea. I keep a small script that I can drop the Access file on to run decompile.

Related

Access add new line to table

I have sub that runs when the database is opened to a specific form and I'm trying to get it to add information in a table.
The table name is UnAuthorizedAccess and the columns in table are ID(auto number), NAME(text), COMPUTERNAME(text), ATTEMPTDATE(date/time).
What commands do I need to use to add a new record to this table? I have a VBA that if they're login information Isn't there is will force close access all together. I'm trying to gather information on the user as well before kicking them out.
I figured this is the easiest way as outlook won't let you send a hidden email from the user unless they first see it.
You can add records to a recordset with the following code, but I am unsure whether you have a field called COMPUTERNAME. You shouldn't need to add the ID value as its an autonumber.
dim Rst as recordset
Set Rst = CurrentDb.OpenRecordset(Name:="UnauthorizedAccess", Type:=RecordsetTypeEnum.dbOpenDynaset)
With Rst
.AddNew
![NAME] = Me.Name.Value
![COMPUTERNAME] = Me.COMPUTERNAME.Value
![ATEMPTDATE] = date()
.Update
End With
As for sending hidden emails, see this question I asked not so long ago. It sends an email via outlook, but remember to reference the Microsoft Outlook Object library in the VBA Editor.
CurrentDB.Execute is the method for executing SQL statements, and INSERT INTO is the SQL statement for adding records to a DB table.
CurrentDB.Execute "INSERT INTO UnAuthorizedAccess (NAME, COMPUTERNAME, ATTEMPTDATE) " & _
"VALUES (" & Your_NAME_Variable & ", " & Your_COMPUTERNAME_Variable & ", " & Now() & ")
Replace Your_NAME_Variable and Your_COMPUTERNAME_Variable with the variables in your code containing these values.

MS Access 2013 copy a specific number of fields and paste into a new record

I have found similar answers to this question, even on this site, however, the syntax has not worked for my database and I'm not sure what needs to be done. This data base is used to house audits for staff performance and accuracy. I am now in the midst of creating the forms and getting them to flow properly for the user.
When conducting an audit, the user will need to enter six specific fields into the first form. Those forms are Audit, Month, Year, Username, Location, Reviewer, and Date. The user will need to complete multiple audits, however, these six fields will always be the same.
I would like to copy these fields in the first form and carry them into the second form so the user does not have to repeat the information. Here is my current code (set to run on the click of a command button on the bottom of screen 1):
Dim strSQL As String
strSQL = "INSERT INTO [tblTripMem] (Audit, Month, Year, Username, Location, Reviewer, Date)"
strSQL = strSQL & " Values (" & Me.cboFP1Audit & "," & Me.Month & "," & Me.Year & "," & Me.Username & "," & Me.Location & "," & Me.Reviewer & "," & Me.Date & ") FROM [FPScreen1]"
strSQL = strSQL & "WHERE (currentrecord = " & Me.CurrentRecord & ")"
DoCmd.RunSQL (strSQL)
Each time I run this I receive the following error: "Invalid SQL statement; expected 'DELETE', 'INSERT', 'PROCEDURE', 'SELECT', or 'UPDATE'.
I am new to Access and am unsure of what this means or how to fix it. All I know is that I'm not finding a solution. Can anyone help? I'd greatly appreciate it.
Here's a mock-up Access file illustrating a way to do what you're doing without using SQL:
With Form 1 open...
...complete the various fields:
Click the Copy to Form 2 button and this will open Form 2 and populate its fields with the data from Form 1:
Here's the VBA code on the Copy to Form 2 button's OnClick event:
Private Sub cmdCopyToFrm2_Click()
Dim frm As Form
DoCmd.OpenForm "Form2"
Set frm = Forms!Form2
With frm
!AuditRef = Me.cboFP1Audit
!AuditMonth = Me.txtAuditMonth
!AuditYear = Me.txtAuditYear
!AuditUsername = Me.txtAuditUsername
!Location = Me.txtLocation
!Reviewer = Me.txtReviewer
!AuditDate = Me.txtAuditDate
End With
End Sub
Note that when Form 2 opens, the textbox that the cursor defaults to might not seem to show any data; if you move away from that textbox it should magically show (don't know why it does this, but there you go).
INSERT INTO table (...) VALUES (...) cannot have a FROM or WHERE clause. You insert a single record with fixed values, not data from another table.
But once you delete these clauses, you will have other errors, because you need to format your string and date values correctly to get the INSERT query to work.
And then you will still be prone to SQL injection errors. It is safer to use Recordset.AddNew and .Update to add records.
See e.g. here: https://stackoverflow.com/a/34969410/3820271
(but without the loop)

MS Access Search Form for Large database

I'm building a database that will be accommodating a large number of records, so I want a search function that is easiest on the server. I am using the following code but I know it isn't sustainable for a larger database. It's looking at the search box and running a query to narrow the search results:
Private Sub SearchFor_Change()
'Create a string (text) variable
Dim vSearchString As String
'Populate the string variable with the text entered in the Text Box SearchFor
vSearchString = SearchFor.Text
'Pass the value contained in the string variable to the hidden text box SrchText,
'that is used as the sear4ch criteria for the Query QRY_SearchAll
SrchText.Value = vSearchString
'Requery the List Box to show the latest results for the text entered in Text Box
'SearchFor
Me.SearchResults.Requery
'Tests for a trailing space and exits the sub routine at this point
'so as to preserve the trailing space, which would be lost if focus was shifted from
'Text Box SearchFor
If Len(Me.SrchText) <> 0 And InStr(Len(SrchText), SrchText, " ", vbTextCompare) Then
'Set the focus on the first item in the list box
Me.SearchResults = Me.SearchResults.ItemData(1)
Me.SearchResults.SetFocus
'Requery the form to refresh the content of any unbound text box that might be feeding
'off the record source of the List Box
DoCmd.Requery
'Returns the cursor to the the end of the text in Text Box SearchFor,
'and restores trailing space lost when focus is shifted to the list box
Me.SearchFor = vSearchString
Me.SearchFor.SetFocus
Me.SearchFor.SelStart = Me.SearchFor.SelLength
Exit Sub
End If
'Set the focus on the first item in the list box
Me.SearchResults = Me.SearchResults.ItemData(1)
Me.SearchResults.SetFocus
'Requery the form to refresh the content of any unbound text box that might be
'feeding off the record source of the List Box
DoCmd.Requery
'Returns the cursor to the the end of the text in Text Box SearchFor
Me.SearchFor.SetFocus
If Not IsNull(Len(Me.SearchFor)) Then
Me.SearchFor.SelStart = Len(Me.SearchFor)
End If
Ideally I want a form that has several search fields, and one 'find' button that runs the queries to return the results in a list box.
I'm also not sure how to set it up so that when the user double clicks on a selection from the search results, the selected record is opened in a form in edit mode.
Any help would be much appreciated, thanks!
First off, you've asked two questions in one post. I recommend you take out the second question regarding opening the selection in edit mode on double click.
As best as I can understand, you're concerned about the performance of your current code as well as the lack of features or flexibility it offers.
Regarding performance:
Don't use the change method to perform the filter. If you really do want to use the change method, use it only to set a timer interval to something like 500 (ms) and then perform the filter on the Timer event. This was the filter won't occur until after the user has stopped typing for a half second.
Avoid "fuzzy" searches (use of asterisk/percent in text fields). It doesn't look like you're using them now. While fuzzy searches usually make software more user friendly, they make it less user friendly when they cause a significant hit on the performance.
When working with large amounts of data, most performance gains come from carefully restructuring the way your application works, by upgrading to SQL Server, and by upgrading your server and network to better hardware. You can only improve about so much when using a JET/ACE backend database container. SQL Server with ADO and ODBC linked tables both offer some advantages over DAO with JET/ACE. ODBC linked tables offer lazy loading while ADO offers things like disconnected recordsets which can be filtered without an additional call back to the server (there are limitations to this).
As already mentioned above, you might need to carefully rethink how your application works and how it is designed. It's better to try to limit the amount of complicated queries that are needed and the amount of text-based searching that is allowed/required. Use more lookup/reference tables. Instead of storing thinks like categories as text, consider storing them as a Long Number CategoryID instead. Queries on indexed numeric fields usually perform better than queries on text-based fields, especially if you are using LIKE with asterisks in your query.
As far as the rest of your question (flexibility and features), consider creating a procedure that builds a criteria/where statement for you based on the values of multiple controls. In a situation such as yours my code would look something like this (below). Notice that I did use asterisk (fuzzy search) in my Description search/filter. If it performs poorly you'll need to consider taking that out and allowing the user to put their own asterisks in instead.
Private Sub cmdSearch_Click()
Call SetRowSource
End Sub
Private Sub txtSearch_AfterUpdate()
Call SetRowSource
End Sub
Private Sub cboCategoryID_AfterUpdate()
Call SetRowSource
End Sub
Private Sub txtBrand_AfterUpdate()
Call SetRowSource
End Sub
Private Sub SetRowSource()
Dim sSQL as String
sSQL = "SELECT ItemID, Description, Brand FROM tblItems "
sSQL = sSQL & GetWhere
Me.lstSearchResults.RowSource = sSQL
End Sub
Private Function GetWhere() as String
Dim sWhere as String
If Nz(Me.cboCategoryID, 0) <> 0 Then
sWhere = sWhere & "CategoryID = " & Me.cboCategoryID & " AND "
End If
If Nz(Me.txtSearch, "") <> "" Then
sWhere = sWhere & "Description LIKE '*" & Replace(Me.txtSearch, "'", "''") & "*' AND "
End If
If Nz(Me.txtBrand, "") <> "" Then
sWhere = sWhere & "Brand = '" & Replace(Me.txtBrand, "'", "''") & "' AND "
End If
If sWhere <> "" Then
sWhere = Left(sWhere, Len(sWhere)-5)
GetWhere = "WHERE " & sWhere
End If
End Function
I think I might be a little bit odd in the Access community but I generally do not allow my controls to reference other controls. In your case the RowSource in your listbox references the controls of the form it's located on. For a variety of reasons, I prefer to build my SQL statements in VBA code, particularly when they are subject to change/filtering. Another thing you might consider doing is using a Datasheet form instead of a listbox. You can set the form's RecordSource and just apply your WHERE statement to the form's Filter property then. Datasheet forms are more flexible for the user as they can resize columns and do sorting without any help from you the programmer. You can always lock the controls so they can't do any editing. When I use datasheets this way I think use the DoubleClick event to allow them to open the record, which is arguably less user friendly then using the single click on a listbox.

How to run a sequence of queries against an Access database [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I would like to create and run a list of predefined queries on several similar databases.
The idea is open the database, run the queries, and then close.
Now I create each of them manually, run and then delete them from each database.
I don't know how to do it in VBA code.
Can anyone drop me a line on how to do it with a simple example?
You can use the Name property for each item in your database's QueryDefs collection to make a list of your saved queries. I think that addresses the title of your question. However the body of your question seems to ask for a lot more as far as I can tell.
You can load a string variable with the text from the SQL property of a QueryDef in your current database. Then use the OpenDatabase method to open another db file, and Execute that string there.
Public Sub RunQueryOnAnotherDb(ByVal pQuery As String, _
ByVal pRemoteDb As String)
Dim dbRemote As DAO.Database
Dim strSql As String
strSql = CurrentDb.QueryDefs(pQuery).SQL
'Debug.Print strSql
Set dbRemote = OpenDatabase(pRemoteDb)
dbRemote.Execute strSql, dbFailOnError
Debug.Print "RecordsAffected: " & dbRemote.RecordsAffected
dbRemote.Close
Set dbRemote = Nothing
End Sub
There's plenty of room to refine that one. You should add error handling for example. But, though quick & dirty, I hope it points you in a useful direction.
I tested it on my system like this, and it works with my db and query names.
Public Sub test_RunQueryOnAnotherDb()
Const cstrQuery As String = "qryTestDelete"
Const cstrRemoteDb As String = "C:\share\Access\0NewScratch.mdb"
RunQueryOnAnotherDb cstrQuery, cstrRemoteDb
End Sub

Can I compare two ms-access files? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking us to recommend or find a tool, library or favorite off-site resource are off-topic for Stack Overflow as they tend to attract opinionated answers and spam. Instead, describe the problem and what has been done so far to solve it.
Closed 9 years ago.
Improve this question
I want to compare two ms-access .mdb files to check that the data they contain is same in both.
How can I do this?
I've done this kind of thing in code many, many times, mostly in cases where a local MDB needed to have updates applied to it drawn from data entered on a website. In one case the website was driven by an MDB, in others, it was a MySQL database. For the MDB, we just downloaded it, for MySQL, we ran scripts on the website to export and FTP text files.
Now, the main point is that we wanted to compare data in the local MDB to the data downloaded from the website and update the local MDB to reflect changes made on the website (no, it wasn't possible to use a single data source -- it was the first thing I suggested, but it wasn't feasible).
Let's call MDB A your local database, and MDB B the one you're downloading for comparison. What you have to check for is:
records that exist in MDB A but not in MDB B. These may or may not be candidates for deletion (this will depend on your particular data).
records that exist in MDB B but not in MDB A. These you will append from MDB B to MDB A.
records that exist in both, which will need to be compared field by field.
Steps #1 and #2 are fairly easily accomplished with queries that use an outer join to find the missing records. Step 3 requires some code.
The principle behind the code is that the structure of all the tables in both MDBs are identical. So, you use DAO to walk the TableDefs collection, open a recordset, and walk the fields collection to run a SQL statement on each column of each table that either updates the data or outputs a list of the differences.
The basic structure behind the code is:
Set rs = db.OpenRecordset("[SQL statement with the fields you want compared]")
For Each fld In rs.Fields
' Write a SQL string to update all the records in this column
' where the data doesn't match
strSQL = "[constructed SQL here]"
db.Execute strSQL, dbFailOnError
Next fld
Now, the major complexity here is that your WHERE clause for each field has to be different -- text fields need to be treated differently from numeric and data fields. So you'll probably want a SELECT CASE that writes your WHERE clause based on the field type:
Select Case fld.Type
Case dbText, dbMemo
Case Else
End Select
You'll want to use Nz() to compare the text fields, but you'd use Nz(TextField,'') for that, while using Nz(NumericField,0) for numeric fields or date fields.
My example code doesn't actually use the structure above to define the WHERE clause because it's limited to fields that work very well comparing concatenated with a ZLS (text fields). What's below is pretty complicated to read through, but it's basically an expansion on the above structure.
It was written for efficiency of updates, since it executes a SQL UPDATE for each field of the table, which is much more efficient than executing a SQL UPDATE for each row. If, on the other hand, you don't want to do an update, but want a list of the differences, you might treat the whole thing differently. But that gets pretty complicated depending on the output,
If all you want to know is if two MDBs are identical, you would first check the number of records in each table first, and if you have one non-match, you quit and tell the user that the MDBs aren't the same. If the recordcounts are the same, then you have to check field by field, which I believe is best accomplished with column-by-column SQL written dynamically -- as soon as one of the resulting SQL SELECTS returns 1 or more records, you abort and tell your user that the MDBs are not identical.
The complicated part is if you want to record the differences and inform the user, but going into that would make this already-interminable post even longer!
What follows is just a portion of code from a larger subroutine which updates the saved query qdfOldMembers (from MDB A) with data from qdfNewMembers (from MDB B). The first argument, strSQL, is a SELECT statement that is limited to the fields you want to compare, while strTmpDB is the path/filename of the other MDB (MDB B in our example). The code assumes that strTmpDB has qdfNewMembers and qdfOldMembers already created (the original code writes the saved QueryDef on the fly). It could just as easily be direct table names (the only reason I use a saved query is because the fieldnames don't match exactly between the two MDBs it was written for).
Public Sub ImportMembers(strSQL As String, strTmpDB As String)
Const STR_QUOTE = """"
Dim db As Database
Dim rsSource As Recordset '
Dim fld As Field
Dim strUpdateField As String
Dim strZLS As String
Dim strSet As String
Dim strWhere As String
' EXTENSIVE CODE LEFT OUT HERE
Set db = Application.DBEngine(0).OpenDatabase(strTmpDB)
' UPDATE EXISTING RECORDS
Set rsSource = db.OpenRecordset(strSQL)
strSQL = "UPDATE qdfNewMembers INNER JOIN qdfOldMembers ON "
strSQL = strSQL & "qdfNewMembers.EntityID = qdfOldMembers.EntityID IN '" _
& strTmpDB & "'"
If rsSource.RecordCount <> 0 Then
For Each fld In rsSource.Fields
strUpdateField = fld.Name
'Debug.Print strUpdateField
If InStr(strUpdateField, "ID") = 0 Then
If fld.Type = dbText Then
strZLS = " & ''"
Else
strZLS = vbNullString
End If
strSet = " SET qdfOldMembers." & strUpdateField _
& " = varZLStoNull(qdfNewMembers." & strUpdateField & ")"
strWhere = " WHERE " & "qdfOldMembers." & strUpdateField & strZLS _
& "<>" & "qdfNewMembers." & strUpdateField & strZLS _
& " OR (IsNull(qdfOldMembers." & strUpdateField _
& ")<>IsNull(varZLStoNull(qdfNewMembers." _
& strUpdateField & ")));"
db.Execute strSQL & strSet & strWhere, dbFailOnError
'Debug.Print strSQL & strSet & strWhere
End If
Next fld
End If
End Sub
Code for function varZLSToNull():
Public Function varZLStoNull(varInput As Variant) As Variant
If Len(varInput) = 0 Then
varZLStoNull = Null
Else
varZLStoNull = varInput
End If
End Function
I don't know if that's too complex to make sense, but maybe it will help somebody.
You can try AccessDiff (paid product). It has the ability to compare the schema, the data, and also access objects. It has a GUI and also a command line interface.
Disclosure: I am the creator of this tool.
Take text dumps of database tables and simply compare the dumped text files using BeyondCompare (or any other text comparison tool). Crude but can work!
I have very good experience with Cross-Database Comparator. It is able to compare structure and/or data.
See the Compare Access databases section at the Microsoft Access third party utilities, products, tools, modules, etc. page at my website.
I've added "table diff" feature to my accdbmerge utility not so long time ago.
I beleive that this answer will not help to solve original question, but it may be helpful for someone faced with the same problem in the future.
If you want to know if the files are identical then
fc file1.mdb file2.mdb
on a DOS command line.
If the files aren't identical but you suspect they contain the same tables and records then the easiest way would be quickly write a small utility that opens both databases and cycles through the tables of both performing a heterogeneous query to extract the Diff between the two files.
There are some tools out there which will do this for you, but they all appear to be shareware.