I want to keep my database performance at the top, reducing the number of objects and creating stuff just temporarily. I have a table that should be automated from the code:
There is a report that needs data from that table - (works fine), so this is the flow:
'-> form is opened -> onClick find-button
'-> table of dates is created -> table of dates is populated
'-> Report is opened -> onReportClose (I want the table of dates to be deleted).
The code is written, it works fine on its own:
CurrentDb.TableDefs.Refresh
DoCmd.DeleteObject acTable, "temp-table"
So I added a macro on report close to run the above code: which is a function. But I get the error below:
Run-time error '3211': The database engine could not lock table
'temp-table' because it is already in use by another person or
process.
I believe this is because the report is probably using it. So I added a ten seconds delay to my code:
Dim PauseTime As Variant
Dim Start As Variant
Dim Elapsed As Variant
PauseTime = 10
Start = Timer
Elapsed = 0
Do While Timer < Start + PauseTime
Elapsed = Elapsed + 1
Loop
CurrentDb.TableDefs.Refresh
DoCmd.DeleteObject acTable, "temp-table"
Instead, It seems to be holding the whole database for 10 seconds and the same issue is still occurring. Any Ideas/suggestions on how to delete this table on report close, or force table delete in Ms-Access (maybe?) or how to work around this are welcomed.
You must clean the RecordSource of the Report, and run the method Requery in the Unload event. Right after that you can DROP the data table.
Private Sub Report_Unload(Cancel As Integer)
'Desvinculo la tabla del reporte
Me.RecordSource = ""
Me.Requery
'Borro las tablas de datos
DoCmd.RunSQL "DROP TABLE name_of_table"
End Sub
Related
I am new on access and what I am trying to do is a select with a criteria so I created a query with the wizard and seted the criteria with a text from a form ([Forms]![Form1]![Transacao]) and created a button to run the query at the first time works great but when I type something else and click the button the datas do not refresh. What I have to do to refresh? I've tryed to add refresh on the event click of the button and did not work.
Thanks in advance for your help.
In Access, a query is usually opened in a default Datasheet view. This default datasheet is contained in a window (or tab) that is only accessible using Macros or DoCmd in VBA.
Once a query window is open, its data will not necessarily update automatically when new records are added to the underlying table(s). The datasheet needs to be "requeried". (Incidentally, the term "refresh" is usually reserved to mean "redrawing" a window on the screen and has nothing to do with the data. This is especially the case in programming and development environments which deal with data and drawing/painting windows and controls on the screen.)
Here is one way to force a query to update its data (when open in its default datasheet view):
DoCmd.OpenQuery "QueryName"
DoCmd.Requery
Calling OpenQuery should also activate the query window if it is already open. If you find that the windows does not activate, you can also call DoCmd.SelectObject acQuery, "QueryName" before DoCmd.Requery.
The DoCmd methods correspond to Macro actions, so if the query is activated by a Macro, just add the Requery action to the macro after the OpenQuery or SelectObject actions. Leave the Control Name parameter of the Requery action blank to force the entire query to updated.
I know this question is a bit stale at this point, but since I couldn't find a suitable answer to this question and the above answer didn't work for me (and still hasn't been accepted), I thought I'd offer my solution for those few poor saps still stuck developing applications in Access. My use case was slightly different (changing the underlying SQL of a query, then opening/refreshing it), but the same principle could be applied. The gist is to first check to see if the query is open and close it if it is. Then open it up again.
To do this, paste this code into a VBA module:
Public Function open_or_refresh_query(query_name As String, Optional sql_str As String = "")
' Refresh or open an existing query
' query_name: Name of the query
' sql_str: optional new SQL string if changing the underlying SQL. If not given,
' the query will open with its existing SQL string
On Error GoTo err_handler
Dim qdf As QueryDef
' Loop through each query in the DB and find the one of interest by name
For Each qdf In CurrentDb.QueryDefs
If qdf.Name = query_name Then
' If the query is open, close it
If SysCmd(acSysCmdGetObjectState, acQuery, query_name) <> 0 Then
DoCmd.Close acQuery, query_name, acSaveNo
End If
Exit For
End If
Next qdf
Set qdf = CurrentDb.QueryDefs(query_name)
' Reset the SQL if new SQL string was given
If Len(sql_str) > 0 Then qdf.sql = sql_str
' Close the QueryDef object to release resources
qdf.Close
' Open the query in default datasheet view
DoCmd.OpenQuery query_name
exit_function:
Exit Function
err_handler:
MsgBox "Error " & Err.Number & ": " & Err.Description, vbCritical, "Error"
Resume exit_function
End Function
At this point you could call this function from any other VBA code in your project. To open/refresh a query from a macro as the OP wanted to do, create a new macro and add a RunCode action with open_or_refresh_query("my_query") in the Function Name field, changing my_query to the name of your query.
I'm using Access as a front end to SQL Server. We have a split form where the users select a record, insert some new information with the form, then continue on to the next record. My manager would like to see how long it takes a user to modify a record. So I would like a timer to start when someone clicks on a record then it to stop when they click on the next record or when they select complete in the form. I would like this imformation exported to an excel sheet. It might be easier to get the average time for each user since most users are modifiying over 100 records per day. Is there anyway this is possible?
the simple solution : you can write a function in the On Dirty event of the form and add a record to a table with current record's ID,Username and current time (starting time).
in the On AfterUpdate of the form use a function to update the same record and add current time as (Ending time).
When a user starts editing or adding a record, the time is registered in this table. when he/she updates the record, the time is added as ending time.
Now you have a table which contains ID, username, startTime, endingTime. with a query, you can calculate how much time is spent for each record, starting from editing, ending to updating. You can export this table to a excel sheet whenever you need.
the complex method:
I had a similar situation. I was asked to close the database if the user is idle for a specified time (for network traffic problems)
I used the bellow function in timer event of the form. It reads the name of the active form every 5 seconds and saves it in a static variable. this function compares Screen.activeform.name with this static variable every 5 seconds.
if the name of active form is different with this static variable, it shows the user is active (doing something), so it resets the timer static variable. otherwise it means the user is idle and adds the timer.
when the timer reaches to a specified amount which is set in application's option (let's say 5 min) it means that the user has been idle for 5 min and I simply run the Application.Quit and close the database.
I'm busy at present and have no time to work on it. You can modify this function to check the current record or even current control (Instead of current form) and before resetting the static timer variable, export the timer variable, username and ID of the record to a table and calculate the spent time with a query and export it to excel.
Hope it helps.
If I find a spare time this weekend, I'll try to make a simple database to show how actually it works
MyTimer:
' IDLEMINUTES determines how much idle time to wait for before
' running the IdleTimeDetected subroutine.
IdleMinutes = fGetOption("Machine", "AutoLogOff", False)
If IdleMinutes = 0 Then Exit Sub
Static PrevControlName As String
Static PrevFormName As String
Static ExpiredTime
Dim ActiveFormName As String
Dim ActiveControlName As String
Dim ExpiredMinutes
On Error Resume Next
' Get the active form and control name.
ActiveFormName = Screen.ActiveForm.Name
If Err Then
ActiveFormName = "No Active Form"
Err = 0
End If
ActiveControlName = Screen.ActiveControl.Name
If Err Then
ActiveControlName = "No Active Control"
Err = 0
End If
' Record the current active names and reset ExpiredTime if:
' 1. They have not been recorded yet (code is running
' for the first time).
' 2. The previous names are different than the current ones
' (the user has done something different during the timer
' interval).
If (PrevControlName = "") Or (PrevFormName = "") _
Or (ActiveFormName <> PrevFormName) _
Or (ActiveControlName <> PrevControlName) Then
PrevControlName = ActiveControlName
PrevFormName = ActiveFormName
ExpiredTime = 0
Else
' ...otherwise the user was idle during the time interval, so
' increment the total expired time.
ExpiredTime = ExpiredTime + Me.TimerInterval
End If
' Does the total expired time exceed the IDLEMINUTES?
ExpiredMinutes = (ExpiredTime / 1000) / 60
'Debug.Print ExpiredMinutes
If ExpiredMinutes >= IdleMinutes Then
' ...if so, then reset the expired time to zero...
ExpiredTime = 0
' ...and call the IdleTimeDetected subroutine.
IdleTimeDetected ActiveFormName
End If
Sub IdleTimeDetected(ActiveFormName As String)
DoCmd.Close acForm, ActiveFormName
CloseAllForms
Application.Echo True
Application.Quit
End Sub
Here's a simple sample database to help you.
Sample Database
This database is written in Office 2013. I hope you can open it.
Open the frm Form and start editing a record or start inserting a new record.
the starting and ending of editing/inserting of a record will be registered in result table. You can export this table to a excel file at the end of the day.
Be careful that this database needs some modifications.
for example you must add some codes to recognize the current username
I think you can manage this section
I have a very complex process that involves downloading a number of files from different shares, concatenating those files into working tables, manipulating and calculating related information, and then exporting specific fields (and calculations) as reports into a number of Excel workbooks.
I have this process coded so that I can click one button and the entire process will execute end to end. I have a series of text boxes that function as 'indicators' (red - this part failed, green - this part succeeded). As you can imagine, the code for the entire process is HUGE (32 pages when copied into MSWord) and difficult to weed through when I have a problem.
I got it into my mind that I wanted to put the code into a table so that it was much more modular and easier to deal with. I have setup a combo box with the action that I want to take and a second combo box with the report/file/object that I want to work with (ie Delete - Table 2, Acquire - File 1, Export - Report 4). I have been successful at creating the SQL statement to do simple things like del * from tbl_test and execute that from the combo boxes without any issue.
What I need to know is if there is a way to put what is essentially a code snippet into the table (memo field) and then have that vba code execute when I select the matching combos.
IE the code for 'Acquire - File1' is completely VBA code; it maps a network drive, locates the file, downloads the file, and moves it to a directory.
IE the code for 'Scrub - tblMain_Part1' is a combination of vba and sql code; it checks for the existence of a file (vba), if it finds it, it deletes a portion of the main table (sql) and appends the contents of the file it finds (sql), then it updates the monitor to indicate that it is completed (vba). If the file is not found, it changes the monitor box to red and updates a command button caption (vba)
I am NOT a genius with vba, but I hold my own. The thought process I had was that if I can essentially get the code broken into managable chunks in the table, I could call the code smippets in order if I want to run the entire process, or I could just re-execute portions of the code as needed by selecting the action and report/file/object combination.
Any thoughts/ideas are appreciated.
I think it would be best to split the code into Subs. The table you loop through would have a Sub-Name field and a blnSuccess field. Your code would loop though the table running each sub and then updating blnSuccess based on any errors you receive. This would give you queryable result set when you try to see what happened.
Consider using macros. You shouldn't need a table. Also, consider moving your hard-coded SQL to queries.
I think that you shouldn't use a table, just create a module with different subs for each operation. On your button event, after the combo selections, I would do a case statement.
dim strOperation as string
strOperation = me!selectionOne
Select Case strOperation
Case "delete": deleteTable(me!selectionTwo)
Case "export": export(me!selectionTwo)
case "acquire": acquire(me!selectionTwo)
End Select
Of course, you'd have your acquire, delete, and export methods written in a module and have whatever parameters you need for each operation there.
This is just one idea of many that you could use to approach this.
I was going to edit the original answer but this seems to be off on a different tack....
I think it would be best to split the code into functions that return a string if there is an error. The table you loop through would have a strFunction,strError and strObject fields. Your code would loop though the table running each function based on the case statement while passing the strObject as a string and then updating strError based on any errors you receive. You could query the table after this process to see which records have errors in them.
If the button is called cmdRunAll here is the code for it.
Private Sub cmdRunAll_Click()
On Error GoTo ErrHandler
Dim rst As DAO.Recordset
Set rst = CurrentDb.OpenRecordset("tblCode", dbOpenDynaset, dbSeeChanges)
If Not rst.EOF Then
With rst
.MoveFirst
Do While Not .EOF
.Edit
Select Case !strFunction
Case "fExport"
!strError = fExport(!strObject)
End Select
.Update
.MoveNext
Loop
End With
End If
rst.Close
Set rst = Nothing
MsgBox "Processes complete"
Exit Sub
ErrHandler:
Debug.Print Err.Description & " cmdRunAll_Click " & Me.Name
Resume Next
End Sub
Here is a simple sample function
Public Function fExport(strTable As String) As String
On Error GoTo ErrHandler
Dim strError As String
strError = ""
DoCmd.TransferText acExportDelim, , strTable, "C:\users\IusedMyUserNameHere\" & strTable & ".txt"
fExport = strError
Exit Function
ErrHandler:
strError = Err.Description
Resume Next
End Function
I have a number of buttons on a form which provide additional information to the user, each using DCount to see if there is any information to display and if so, opening a popup form to display it. All had been working well but now for one particular button, it is taking anything between 30 seconds and a minute to open the popup form, which is obviously unacceptable. Can't understand why it worked fine originally but has now gone so slow. All the other buttons still open their form in under a second. VBA is:
Private Sub btnNotes_Click()
'open the popup notes for the current record, if there are associated records
If DCount("ID","qlkpIDForNotes") = 0 Then
MsgBox "There are no notes for this patient", vbOKOnly, "No information"
Else
DoCmd.OpenForm "fsubNotes",,,"ID = " & Me.displayID
End If
End Sub
The table being queried has approx 40,000 rows, where the largest table checked for the other buttons has about 12,000. Have tried doing the DCount directly on the table rather than through a query, but doesn't make any difference. Also tried taking out a section of the data from the original table, copying about 1100 rows into a new table and testing on that. It still took 12 seconds to open.
Any ideas, anyone?
Using DCount() just to find out if there are any row in the table or query can be rather inefficient since DCount() will have to run the whole query and go through all records to return the total count just so you can compare that to 0.
Depending on the complexity of that query, and the joins in it, and whether the joins use fields that have indexes or not, the cost of having to run that query can be exponentially proportional to the number of records in the underlying tables.
To solve your issue try this:
make sure there is an index on the underlying table's ID field in the qlkpIDForNotes query, and that all fields used in the JOIN or WHERE clauses also have indexes.
check if you can use the main underlying table or use a simplified query just to test if there are records that may be returned by qlkpIDForNotes, in short, you may not need to run that query in full just to find out if it would have some records.
use a separate function such as HasAny() below instead of DCount() when you only need to find out if a query returns any results:
'-------------------------------------------------------------------------'
' Returns whether the given query returned any result at all. '
' Returns true if at least one record was returned. '
' To call: '
' InUse = HasAny("SELECT TOP 1 ID FROM Product WHERE PartID=" & partID) '
'-------------------------------------------------------------------------'
Public Function HasAny(ByVal selectquery As String) As Boolean
Dim db As DAO.database
Dim rs As DAO.RecordSet
Set db = CurrentDb
Set rs = db.OpenRecordset(selectquery, dbOpenForwardOnly)
HasAny = (rs.RecordCount > 0)
rs.Close
Set rs = Nothing
Set db = Nothing
End Function
With this, you can simply rewrite your code as :
Private Sub btnNotes_Click()
'open the popup notes for the current record, if there are associated records '
If Not HasAny("qlkpIDForNotes") Then
MsgBox "There are no notes for this patient", vbOKOnly, "No information"
Else
DoCmd.OpenForm "fsubNotes",,,"ID = " & Me.displayID
End If
End Sub
i am trying to create a Report based on a query which will be made on each
creation / update of my DataBase
i attach the report to a button with a macro,
problem is that if nothing was changed and someone just press over the button the report would be fired but with no data, and if the data was inserted to the text boxes and i press over the button with the macro the data does not append to the data base.
my macro lines:
OnError Next,
OpenReport Accept, Report, , , Normal // Accept is the name of the report
GoToRecord Next,
[MacroError]<>0 =[MacroError].[Description], Yes, None,
Thanks in advance for your help
A good way to test and track this would be by adding a 'Yes/No' field called for example "flgReported" in to one of the tables used in the query, then after running your report you could update all rows to true. Any new rows added afterwards would then be false and you could bring this in to your select query to only report on False rows, this would also allow you to test whether there was any data to return when clicking the button before going ahead and running the report.
Update: If you highlight the macro in question and then goto "Tools > Macro > Convert Macro's to Visual Basic" and then click 'Convert' it will generate the VB code for you. This could then be called from your button instead of the Macro and before the main procedure you could insert this to check if there were any new records:
Private Sub ButtonClick() "For Example"
Dim rs As Recordset
Set rs = CurrentDb.OpenRecordset("SELECT * FROM queryname where flgReported = False")
If rs.BOF And rs.EOF then 'No Records to report so we quit at this point
rs.close
set rs = Nothing
Exit Sub
End If
'Remainder of Macro Code goes here
'Then run a simple SQL here to update the flags to True
End Sub