Unable to update table through CurrentDb.Execute - ms-access

Ok, before anyone tells me that it's a duplicate of another question, I don't think it's the same thing. I'm running a CurrentDb.Execute in VBA in MS Access 2007 and it's after I put the option dbFailOnError that I received this error message:
Error 3218 Could not update; currently locked
Before this, nothing was being done on the table I'm trying to update. Here's the current code in the form module:
Option Compare Database
Private Sub cmdvalidate_Click()
Dim user As String
Dim rs As Recordset
Dim strsql As String
Dim pass As String
Dim f1 As Form_frmMenu
strsql = "SELECT * FROM account WHERE username = '" & Me.txtusername & "'"
'Execute SQL and store in recordset (cannot be stored in a string or integer)
Set rs = CurrentDb.OpenRecordset(strsql)
'Go through recordset and extract first value
If rs.RecordCount > 0 Then
rs.MoveFirst
rs.Edit
pass = rs!Password
' I know it's not the best way to validate password here (since it is
' case insensitive, but it's only for internal use with 3 people in
' the same department. This will considered if there are more people
' who get involved, but for the time being, this is not important.
If pass = Me.txtpassword Then
user = rs!UserName
' MS Access debugger says the error is here
CurrentDb.Execute "UPDATE [Account] SET [Account].[Active] = 'Y' WHERE [Account].[Username] = '" & user & "'", dbFailOnError
MsgBox "Login Successful!"
'DoCmd.OpenForm "frmMenu", , , , , acDialog
Else
MsgBox "Incorrect Username Or Password. Please try again."
End If
Else
MsgBox "Something has gone wrong. Please contact your administrator."
End If
End Sub
My goal at the end of the day is to be able to get the username who logged in. I first tried to use a global variable but it seems that this is not possible with different form modules. So instead, I created the Active field so that I could get the username in other form modules through a SELECT statement.
Some checks I did:
The file is not opened by anyone besides me, I'm 100% sure of it.
The query was initially like below but I added the square brackets just in case there were reserved words:
"UPDATE Account SET Active = 'Y' WHERE Username = '" & user & "'"
Other form modules in the same file do not have this UPDATE issue and there are no other options other than Option Compare Database (the other stuff are Private Subs).
I discovered that I could use Environ("Username") to get the Windows login username in the other modules instead. This is will solve the problem, but I'm still wondering why this query cannot execute the update.
Questions:
If the table is locked, is there a way I can unlock it?
Why is this query getting an error in the first place? What has locked it?
I don't know much, if at all about vb and I'm not even sure how I got on this project...

1.) If the table is locked, is there a way I can unlock it?
I'm not so sure the entire table is locked. Perhaps only the current recordset row is locked. See #2.
2.) Why is this query getting an error in the first place? What has locked it?
The code does rs.Edit. Later, when attempting to UPDATE the table directly, the targeted row is the same row which is currently locked in the recordset. However, there is a lot more code there, which makes it difficult to pin down which statements contribute to the error.
This simplified code sample should clarify what's going on.
strSql = "SELECT * FROM account WHERE username = 'hans'"
Set rs = CurrentDb.OpenRecordset(strSql)
rs.MoveFirst
rs.Edit
CurrentDb.Execute "UPDATE Account SET Active = 'Y' WHERE Username = 'hans'", dbFailOnError
As written, that code triggers error 3218, "Could not update; currently locked", on my system.
This change disables the Edit statement and allows the UPDATE to execute successfully.
'rs.Edit
One way you found to avoid the error was by adding rs.Close before executing the UPDATE. That worked because closing the recordset released that edit lock; if you had actually changed values in that row, you would have needed rs.Update to save them before rs.Close.
However, in the original version, you were only reading values from the recordset, not changing any of them. So rs.Edit was not needed.

Huh, I think I just found out what was causing the error.
Apparently, when a RecordSet is using a table (here the line Set rs = CurrentDb.OpenRecordset(strsql)), the table can be accessed only through the RecordSet and not through CurrentDb.Execute anymore (at least, to update).
Two workarounds I found for my specific situation:
Replace the CurrentDb.Execute by something to update the RecordSet itself (Probably easier):
rs!Active = "Y" ' Change the value of Active
rs.Update ' Update the changes to the RecordSet and thus, table
rs.Close ' Close recordset
Set rs = Nothing ' Unset recordset
Close the RecordSet first, then use CurrentDb.Execute:
rs.Close
Set rs = Nothing
CurrentDb.Execute "UPDATE Account SET Active = 'Y' WHERE Username = '" & user & "'", dbFailOnError

Related

vba code to find a blank reord hanging up

I have an access database that I have employer information. Such as name, address, phone number, contact person, business type, etc. There are multiple people entering data & occasionally there is a blank record, because there was an entry started & not finished.
I am trying to use VBA to find the blank record, so I can enter new data in the blank record. Here is the current code.
Private Sub Command107_Click()
If DCount("*", "Blank Query") = "0" Then
MsgBox ("No blank records found. Thank You!")
Else
DoCmd.GoToRecord , , [Queries]![Blank Query2], offset:=1
End If
End Sub
Currently if there is a blank record it hangs on
DoCmd.GoToRecord , , [Queries]![Blank Query2], offset:=1
If there is no blank records, it runs & give the appropriate response.
The only field I am concerned about is, name and address.
Ideally, I would like to have it go to the blank record.
You could use this code:
Dim rst As DAO.Recordset
Dim strSQL As String
strSQL = "select * from tblContacts where " & _
"IsNull([Name]) or IsNull(Address)"
Set rst = CurrentDb.OpenRecordset(strSQL)
If rst.RecordCount <> 0 Then
Me.Recordset.FindFirst "id = " & rst!ID
Else
MsgBox "no blank reocrds found"
End If
rst.Close
DoCmd.GoToRecord will only work if your form is based on your query [Blank Query2].
Apart from that, your syntaxis is incorrect, referring to https://msdn.microsoft.com/en-us/library/office/ff194117.aspx
If this issue occurs on a regular basis, I'd create a form based on [Blank Query2] and add this form to your maintenance / application management frontend (assuming you've split your database into a backend and frontend).
This will give you the ID of the first incomplete Record
RecordID=Nz(DLookup("TablePrimaryKey","TableName","FieldThatCouldBEEmpty IS NULL"),0)
assuming your table has a proper Primary Key. If it does not you can request any field that would help you identify the record, just be sure to change 0 to an appropriate "no record found" value for whatever field you are looking up.
This will just delete any reocrds that are incomplete.
DoCmd.RunSQL "DELETE FROM TableName WHERE FieldThatCouldBEEmpty IS NULL"

access 2010 bloat with forms based on pass through queries

My access 2010 database has super massive bloat. It has gone from 44mb to 282mb in just a few runs of the VBA. Here is what I have set up:
Local tables - these calculate statistics on forms and generally don't move around too much.
Pass through queries - my personal suspect. While viewing a form, if the user clicks on a record I run a pass through query using the record the user clicked on. So user clicked on "joe", pass through query runs with sql string = "select * from sqlserver where name= " &[forms]![myform]![firstname]
After this query runs, my vba DELETES the pass through query, then recreates it after another record is selected. so the user goes back to the list of names, and clicks BRIAN. then my vba deletes the pass through query and creates another one to select all records named brian from sql server, using the same code as above.
Forms - my forms are using the pass through queries as sources.
Is what I'm doing not very smart? How can I build this better to prevent access from exploding in file size? I tried compact and repair, as well as analyze DB performance in access 2010. Any help at all is appreciated, I've been googling access2010 bloat and have read about temptables as well as closing DAO (which I am using, and which I did explicitly close). Thanks!
Here is some code for 1 of the forms i'm using -
Private Sub name_Click()
'set dims
Dim db As DAO.Database
Dim qdExtData As QueryDef
Dim strSQL As String
Dim qdf As DAO.QueryDef
'close form so it will refresh
DoCmd.Close acForm, "myform", acSaveNo
'delete old query so a new one can be created with the same name
For Each qdf In CurrentDb.QueryDefs
If qdf.Name = "QRY_PASS_THROUGH" Then
DoCmd.Close acQuery, "QRY_PASS_THROUGH", acSaveNo
DoCmd.SetWarnings False
DoCmd.DeleteObject acQuery, "QRY_PASS_THROUGH"
DoCmd.SetWarnings True
Exit For
End If
Next
Set db = CurrentDb
'sql for the data
strSQL = "select fields from (table1 inner join table2 on stuff=stuff and stuff=stuff) left join table3 on stuff=stuff and stuff=stuff where flag='P' and table.firstname = " & [Forms]![myform]![firstname]
Set qdExtData = db.CreateQueryDef("QRY_PASS_THROUGH")
'how you connect to odbc
qdExtData.Connect = "ODBC;DSN=server;UID=username;PWD=hunter2;"
qdExtData.SQL = strSQL
DoCmd.OpenForm ("names")
Forms!returns!Auto_Header0.Caption = "Names for " & Me.name & " in year " & Me.year
qdExtData.Close
db.Close
qdf.Close
Set db = Nothing
Set qdf = Nothing
End Sub
There no reason I can think of to not bind the form to a view and use the “where clause” of the open form command. It would eliminate all that code.
You could then simply use:
strWhere = "table.FirstName = '" & me.FirstName & "'"
Docmd.OpenForm "Names”,,,strWhere
Also, it makes little or no sense that a C + R does not return free space. Something else here is seriously wrong.
Also, you really don’t need to delete the query each time. Just assume that the pass-through ALWAYS exists and then use this:
strSQl = “your sql goes here as you have now”
Currentdb.Querydef("MyPass").SQL = strSQL
Docmd.Openform “your form”
The above is all you need. I count about 3 lines of code here that will replace all what you have now. Note that of course the connection string info is saved with the pass-though and does not need to be set or messed with each time.
I would also do a de-compile of your database. I have a short cut setup on all my dev machines so I can just right click to de-compile. Here is some info on de-compile:
http://www.granite.ab.ca/access/decompile.htm
So really, I don’t know why you not using the where clause of the open form? Just bind the form to a SQL view without any conditions. Then use the open form command – you only pull records down the network pipe that match your criteria.
And note how you can stuff your SQL directly into the .SQL property of the query def as above shows – again no need for that delete code and this also means you don’t mess with connection info in code either. Again about 3 lines in total.

Error 3622 - You must use the dbSeeChanges option with OpenRecordset when accessing a SQL Server table that has an IDENTITY column

I'm running MS Access 2007, connecting to a MS SQL 2008 R2 server. I have a form with a multiselect box that I use to update status for several fields for the servers selected. Currently I'm using the ServerName field in the multiselect box. The problem is that there can be multiple records with the same server name. So to get around this I want to change it from ServerName to record number (ID). When I do this though VB gives the error "Error 3622 - You must use the dbSeeChanges option with OpenRecordset when accessing a SQL Server table that has an IDENTITY column". I've looked at many different posts on different websites, but can't figure out how to implement this into my code. The script works perfectly when I use ServerName, it also works perfectly if I go to the SQL server and change the field to Identity = False. The problem with that is that I can't create new records with the autonumber. It also works perfectly if I hard code the line numbers in an Update query. The problem with that is I can't hardcode the line numbers for everyone that uses the database. The issue only appears to be related to VB.
Below is what I have currently. As you can see I tried adding the dbSeeChanges to the Execute line.
Private Sub btnRoarsStatus_Click()
Dim strSQL As String
Dim Criteria As String
Dim Itm As Variant
With Me.lstServerNames
If .ItemsSelected.Count > 0 Then
For Each Itm In .ItemsSelected
Criteria = Criteria & "," & .ItemData(Itm)
Next Itm
' remove leading comma
Criteria = Mid(Criteria, 2)
' execute the SQL statement
strSQL = "UPDATE buildsheet SET [Roars Status] = " & Chr(34) & _
Me.cboRoarsStatus & Chr(34) & " WHERE ID IN(" & Criteria & ")"
Debug.Print strSQL
CurrentDb().Execute strSQL, dbSeeChanges
Else
MsgBox "You must select one or more items in the list box!", _
vbExclamation, "No Selection Made"
Exit Sub
End If
End With
MsgBox "Completed", vbExclamation, "Completed"
End Sub
I had a similar Problem with a Delete Statement that I wanted to execute and solved it by:
CurrentDb.Execute SqlStr, dbSeeChanges
note the missing () after CurrentDb
For me worked this:
CurrentDb.Execute strSQL, **dbFailOnError +** dbSeeChanges
dbFailOnError was the key...
CurrentDb().Execute strSQL, someotherConstant, dbSeeChanges
I think, someotherConstant maybe missing.

DAO.Recordset.Update results in reckord lock

I am trying to run the following code to loop around a recordset and do updates where neccessary.
I have a Microsoft Access database connected to a MySql backend. Whenever I run this code I get the following error:
3197 error: The Microsoft Office Access database engine stopped the process because you and another user are attempting to change the same data at the same time.
The code is below:
Private Sub test()
Dim rs As DAO.Recordset, rsCnt As Long, i As Long
Set rs = CurrentDb.OpenRecordset("qryMyQuery", DB_OPEN_DYNASET)
rs.MoveLast
rsCnt = rs.RecordCount
rs.MoveFirst
For i = 1 To rsCnt
rs.Edit
rs!MyFieldInTable = "test"
rs.Update
Next i
End Sub
I thought the Access database might be corrupt so I pulled an earlier backup but it's doing the same thing which makes me think it's a MySql issue.
We use an identical piece of code on another version of this database linked to a different MySql table and it works fine.
Also, when I open the query the record-set is based on I can edit the data in the query without any issues.
Just to add, on the first loop, rs!MyFieldInTable is updated, then I get the error.
It does not appear that you are moving to another record in the recordset. Simply incrementing i doesn't move to the next record. A more traditional approach would be to iterate over the recordset without the need for your other variables (i and rsCnt).
Dim rs as DAO.Recordset
Set rs = CurrentDb.OpenRecordset("qryMyQuery", DB_OPEN_DYNASET)
rs.moveFirst
Do Until rs.EOF
rs.Edit
rs!FieldNameHere = "test"
rs.Update
rs.MoveNext
Loop
EDIT
After a bit of searching I came across this thread which seems to be similar to your issue. At the bottom of the thread a suggestion is made to modify the ODBC settings for your MySQL DSN by selecting the "Advanced" tab and selecting the option to "Return Matching Rows". The post also says to drop the linked table and then re-link it to your Access database.
I haven't used Access with MySQL in the past, so I have no idea whether this will work or not, so proceed with caution!
You may also try changing your recordset to use the dbOptimistic flag for the recordset locking option to see if that helps at all:
set rs = CurrentDB.OpenRecordSet("qryMyQuery", DB_OPEN_DYNASET, dbOptimistic)
Two things you can try. First, try adding the dbSeeChanges option when opening the recordset:
Dim rs as DAO.Recordset, db As DAO.Database
Set db = Currentdb
Set rs = db.OpenRecordset("qryMyQuery", dbOpenDynaset, dbSeeChanges)
Do Until rs.EOF
rs.Edit
rs!FieldNameHere = "test"
rs.Update
rs.MoveNext
Loop
The other option, as #HansUp suggested, is to use a SQL update statement instead of a dynamic recordset. The key there is to open the recordset as a snapshot, so that changes you make to the records do not affect the recordset itself.
Dim rs as DAO.Recordset, db As DAO.Database
Set db = Currentdb
Set rs = db.OpenRecordset("qryBatchPayments", dbOpenSnapshot)
Do Until rs.EOF
db.Execute "UPDATE Payments " & _
"SET DCReference='test' " & _
"WHERE PaymentID=" & !PaymentID, dbFailOnError
rs.MoveNext
Loop
I was having the same problem and my solution turned out to be the default value for BIT(1) fields. Access does not like these to be null. Make sure you use either 0 or 1 in mysql for these fields.
I don't have MySQL here to try this against, but it looks to me as if your code is not advancing the recordset after the rs.Update method is executed, so that you are trying to udate the same field in the fierst record.
Add this line after the rs.Update:
rs.MoveNext
Hope that helps.
Try calling OpenRecordset from an object variable set to CurrentDb(), rather than directly from CurrentDb().
Dim rs as DAO.Recordset
Dim db As DAO.Database
Set db = Currentdb
Set rs = db.OpenRecordset("qryMyQuery", DB_OPEN_DYNASET)
rs.moveFirst
Do Until rs.EOF
rs.Edit
rs!FieldNameHere = "test"
rs.Update
rs.MoveNext
Loop
The reason for that suggestion is I've found operations on CurrentDb directly can throw an error about "block not set". But I don't get the error when using an object variable instead. And ISTR OpenRecordset was one such operation where this was an issue.
Also, my impression was your approach is a cumbersome way to accomplish the equivalent of:
UPDATE qryMyQuery SET FieldNameHere = "test";
However, I suspect the example is a proxy for a real world situation where the recordset approach is useful. Still that makes me wonder whether you would see the same or a different error when executing the UPDATE statement.
If you continue to have trouble with this, it may help to show us the SQL View for qryMyQuery.
I have discovered that if one tries to save data which are the same as the one already in the MySql record Access will display this kind of error. I've tried some suggestions from this thread but did not help.
The simple solution for this is to save a slightly diffrent data by using a manual time-stamp. Here is an example of heaving a sort order field and setting it to 10, 20, 30...
i = 10
timeStamp = Now()
Do Until Employee.EOF
Employee.Edit
Employee!SortOrderDefault = i
Employee!LastUpdated = timeStamp
Employee.Update
i = i + 10
Employee.MoveNext
Loop
I've tried automatic time-stamp in the MySql table but did not help when the new entry data is the same as the old one.
My little helpful hint is, bits are very, very, very bad data types to use when linking SQL tables to Microsoft Access because only SQL Server understands what a bit is, Microsoft Access has a hard time interpreting what a bit is. Change any bit datatypes to int (integers) and relink your tables that should clear things up. Also, make sure your Booleans always contain a 1 or a 0 (not a yes/no or a true/flase) in your VBA code or your updates will fail to the linked SQL tables because Microsoft Access will try to update them with a True/False or a Yes/No and SQL will not like that.
I also had same problem; i solved them adding those to code using dao.recordset:
**rst.lockedits = true**
rst.edit
rst.fields(...).value = 1 / rst!... = 1
rst.update
**rst.lockedits = false**
this seems fix conflict between just opened data (such as in a form) and updating them with code.
Sorry for my bad english... i read a lot but i never had learn it! I'm just italian.

Access 2007 Runtime Error

I'm not sure if this is the right site to post this question, but here it goes...
In Access 2007 I get the error "Runtime Error '3061': Too few parameters. Expected 1" on this piece of VBA code:
Private Sub btnCheck_Click()
Dim rs As Recordset
Dim db As Database
Dim id As String
Dim query As String
MsgBox ("one")
Set db = CurrentDb()
id = Me.UniqueID.Value
query = "SELECT [Unique_ID] from tblPatients WHERE [Unique_ID] =" & id
MsgBox (id)
Set rs = db.OpenRecordset(query) <<<<<HIGHLIGHTED LINE
If IsNull(rs) Then
Me.lblCheck.Caption = "NEW"
Else
Me.lblCheck.Caption = "EXISTS"
End If
End Sub
The data source is a table, not a query. Any help would be much appreciated!
There is no field named Unique_ID in your table tblPatients. If you posted all of your code then that is the only possible explanation.
EDIT: Your comment confirmed my suspicions:
I just triple checked :P Table name: tblPatients Column name: Unique ID
You added an underscore in your code that did not exist in your field name. You are correct in using square brackets, but the correct code should be:
query = "SELECT [Unique ID] from tblPatients WHERE [Unique ID] =" & id
Please note the removed underscores. Alternatively (and I'd say preferably if you are in the early stages of design), you can rename the field in the table to either Unique_ID or UniqueID and save yourself a good deal of hassle.
A Few things can cause this error. A common error is misspelled table names and field names.
I would check tblPatients is spelled correctly or that there is no prior suffix like dbo.tblPatients required if the table is linked to a Server Connection.
As well we are assuming the id is a number and isn't a text field which would cause an error if you do not have the correct quotes. ie.
it would instead read
query = "SELECT [Unique_ID] from tblPatients WHERE [Unique_ID] = '" & id & "';"
Lastly, try to place ";" like I did in the line above.
I suggest you add a Debug.Print statement to your code like this:
query = "SELECT [Unique_ID] from tblPatients WHERE [Unique_ID] =" & id
Debug.Print "query: " & query
The reason for that suggestion is Debug.Print will print your SQL statement to the Immediate Window. (You can use the Ctrl+g keyboard shortcut to get to the Immediate Window.) Then you can view the completed string you're asking OpenRecordset to use. Often just seeing that string (rather than trying to imagine what it should look like) will let you spot the problem. If not, you can copy the string from the Immediate Window and paste it into SQL View of a new query ... the query designer can help you pinpoint syntax errors ... or in this case, I think it may alert you to which item in your query the database engine doesn't recognize and suspects must therefore be a parameter. And if that step still doesn't resolve the problem, you can paste the string into your question on Stack Overflow.
Finally, I think you may have a logic error with IsNull(rs) ... because rs has been declared a recordset, it will never be Null. In the following example, the SELECT statement returns no records. And the Debug.Print statement says IsNull(rs): False both before and after OpenRecordset.
Public Sub RecordsetIsNeverNull()
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim strSql As String
strSql = "SELECT * FROM tblFoo WHERE 1 = 2;"
Set db = CurrentDb
Debug.Print "IsNull(rs): " & IsNull(rs)
Set rs = db.OpenRecordset(strSql)
Debug.Print "IsNull(rs): " & IsNull(rs)
rs.Close
Set rs = Nothing
Set db = Nothing
End Sub
Edit: According to Problem names and reserved words in Access, query is an Access reserved word. I don't actually think that is the cause of your problem, but suggest you change it anyway ... perhaps strQuery.