Generating a Report from Query; capturing data from several tables. The Report has two calculated boxes and I want to UPDATE the data back to one of the tables. Debugging shows I'm capturing the variables but keeps giving me Syntax errors in the WHERE clause. I've tried lots of syntax iterations from scouring the net.
Private Sub Report_Load()
Dim sqls As String
Dim TEP As Single
Dim PPS As Single
Dim RecipeN As String
TEP = Reports![RecipeBuild]![txtTEP]
PPS = Reports![RecipeBuild]![txtPPS]
RecipeN = Reports![RecipeBuild]![RecipeName]
sqls = "Update [tblRecipeBuild] " _
& "Set TEP = " & TEP & " " _
& "Set PPS = " & PPS & " " _
& "WHERE [RecipeName] = '" & RecipeN & "';"
DoCmd.SetWarnings False
DoCmd.RunSQL sqls
DoCmd.SetWarnings True
End Sub
An Access SQL UPDATE should include the SET keyword only once.
When you want to update more than one field, use SET once, and then use a comma between the pairs of FieldName=Value segments.
sqls = "Update [tblRecipeBuild] " _
& "Set TEP = " & TEP & ", PPS = " & PPS & " " _
& "WHERE [RecipeName] = '" & RecipeN & "';"
I think that should work but suggest you consider a parameter query instead of concatenating values into an UPDATE statement.
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim strUpdate As String
strUpdate = "UPDATE tblRecipeBuild SET TEP=pTEP, PPS=pPPS WHERE RecipeName=pRecipeN;"
Debug.Print strUpdate
Set db = CurrentDb
Set qdf = db.CreateQueryDef(vbNullString, strUpdate)
With qdf
.Parameters("pTEP").Value = TEP
.Parameters("pPPS").Value = PPS
.Parameters("pRecipeN").Value = RecipeN
End With
qdf.Execute dbFailOnError
Related
Help! i am having some trouble with my access codes on a database whereby it says that access error 3061 Too Few parameters expected 1. Problem highlighted was
Set oRS = CurrentDb.OpenRecordset(sSQL)
Dim i As Date, n As Integer, oRS As DAO.Recordset, sSQL As String
Dim db As DAO.Database
Set db = CurrentDb
Dim BookedDate As Date
Dim FacilitiesID As String
Dim StartTime As Date
cboTime.RowSourceType = "Value List"
cboTime.RowSource = ""
If IsNull(Start) Then Exit Sub Else i = Start
If Me.NewRecord = True Then
DoCmd.RunCommand acCmdSaveRecord
End If
sSQL = "SELECT FacilitiesID, StartTime, BookedDate"
sSQL = sSQL & " FROM qrysubform"
sSQL = sSQL & " WHERE FacilitiesID= " & Me.FacilitiesID & _
" AND BookedDate=# " & Me.txtDate & "#"
Set oRS = CurrentDb.OpenRecordset(sSQL)
Your Facilities ID is dimensioned as a string, though in your SQL statement it is referenced as a number. If your form's FacilitiesID is in fact a string, you need to enclose it in quotes:
sSQL = "SELECT FacilitiesID, StartTime, BookedDate"
sSQL = sSQL & " FROM qrysubform"
sSQL = sSQL & " WHERE FacilitiesID= '" & Me.FacilitiesID & _
"' AND BookedDate=#" & Me.txtDate & "#"
In such cases, insert a debug line and study the output:
Debug.Print sSQL
' Study output
Set oRS = CurrentDb.OpenRecordset(sSQL)
That said, this error is typically caused by a missing or misspelled field name.
I've inherited some Access VBA code and there are controls on a form (such as a listbox named lstOrderID, mentioned below) which have no RowSource property set (an empty string). I look in the code and find statements like this in various places:
Forms!frm_Customer.lstOrderID = rstCust!OrderID ' set from a record set
Forms!frm_Customer.lstOrderID.Requery
Me.lstOrderID = Me.lstOrderID.ItemData(0) ' set to first item in self
But nowhere in the code is lstOrderID.RowSource being set.
How can Requery be called on a listbox that has no RowSource?
How can a listbox be set to a single value (rstCust!OrderID) from a record set, unless this is a list of values (although the debugger shows an integer in lstOrderID.Value)?
Here is more code:
Dim rstCust As Recordset
Set db = CurrentDb
Set rstCust = db.OpenRecordset("SELECT * FROM Orders WHERE CustID=" & ID & _
"AND Datetaken =Date() " & _
"AND VendorID='" & Forms!frm_Customer.cboVendorID & "'")
Forms!frm_Customer.lstOrderID = rstCust!OrderID
rstCust.Close
db.Close
Another section:
Dim rstCust As Recordset
Dim blStatus As Boolean
Dim strSql As String
Set db = CurrentDb
strSql = "SELECT Orders.OrderID " & _
"FROM Orders " & _
"WHERE (((Orders.DateTaken)=#" & Date & "#) " & _
"AND ((Orders.VendorID)='" & Forms!frm_Customer.cboVendorID & "') " & _
"AND ((Orders.CustID)=" & ID & "));"
Set rstCust = db.OpenRecordset(strSql)
Forms!frm_Customer.lstOrderID = rstCust!OrderID
Forms!frm_Customer.lstOrderID.Requery
Forms!frm_Customer.lstOrderID = rstCust!OrderID
rstCust.Close
db.Close
Also this:
Me.lstOrderID.Requery
Me.lstOrderID = Me.lstOrderID.ItemData(0)
Error: "Run-time error '3061' Too few parameters. Expected 2.
I wrote this simple function that returns the remaining percentage calculated for number of records changed. It is supposed to occur when the user updates the field called 'percentage' I am certain the code below should work, but obviously something is wrong. It occurs on the line:
Set rs = db.OpenRecordset("SELECT Tier1, [Percentage], Tier3 AS Battalion, Month " _
& "FROM tbl_CustomPercent " _
& "WHERE (((Tier1)=[Forms]![frmEntry]![cmbImport_T1]) AND ((Month)=[Forms]![frmEntry]![cmbMonth]));", dbOpenSnapshot)
I wonder how it could fail when the very same query is what populates the 'record source' for the form with the 'percentage' textbox.
Function RemainingPercentAvailable() As String
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim strSQL As String
Set db = CurrentDb
Set rs = db.OpenRecordset("SELECT Tier1, [Percentage], Tier3 AS Battalion, Month " _
& "FROM tbl_CustomPercent " _
& "WHERE (((Tier1)=[Forms]![frmEntry]![cmbImport_T1]) AND ((Month)=[Forms]![frmEntry]![cmbMonth]));", dbOpenSnapshot)
Dim CurrentTotal As Single
CurrentTotal = 0
If Not (rs.EOF And rs.BOF) Then
rs.MoveFirst
Do Until rs.EOF = True
CurrentTotal = CurrentTotal + rs!Percentage
rs.MoveNext
Loop
End If
RemainingPercentAvailable = "Remaing available: " & Format(1 - CurrentTotal, "0.000%")
Set rs = Nothing
Set db = Nothing
End Function
Adapt your code to use the SELECT statement with a QueryDef, supply values for the parameters, and then open the recordset from the QueryDef.
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim rs As DAO.Recordset
Dim strSQL As String
strSQL = "SELECT Tier1, [Percentage], Tier3 AS Battalion, [Month] " _
& "FROM tbl_CustomPercent " _
& "WHERE (((Tier1)=[Forms]![frmEntry]![cmbImport_T1]) AND (([Month])=[Forms]![frmEntry]![cmbMonth]));"
Set db = CurrentDb
Set qdf = db.CreateQueryDef(vbNullString, strSQL )
' supply values for the 2 parameters ...
qdf.Parameters(0).Value = Eval(qdf.Parameters(0).Name)
qdf.Parameters(1).Value = Eval(qdf.Parameters(1).Name)
Set rs = qdf.OpenRecordset
Note: Month is a reserved word. Although that name apparently caused no problems before, I enclosed it in square brackets so the db engine can not confuse the field name with the Month function. It may be an unneeded precaution here, but it's difficult to predict exactly when reserved words will create problems. Actually, it's better to avoid them entirely if possible.
This one is calling a query directly in a DAO.Recordset and it works just fine.
Note the same 'Set rs = db.OpenRecordset(strSQL, dbOpenDynaset) This is a parameter SQL as well.
The only difference is with this one is that I DIDN'T need to move through and analyze the recordset - but the error occurs on the 'Set rs = " line, so I wasn't able to get further anyway.
Dim rs As DAO.Recordset
Dim db As DAO.Database
Dim strSQL As String
strSQL = "SELECT Sum(tbl_SP.AFP) AS AFP_TOTAL, tbl_SP.T1_UNIT " _
& "FROM tbl_SP " _
& "GROUP BY tbl_SP.T1_UNIT " _
& "HAVING (((tbl_SP.T1_UNIT)='" & strUnit & "'));"
Set db = CurrentDb
Set rs = db.OpenRecordset(strSQL, dbOpenDynaset)
AFP_Total = rs!AFP_Total
There is an even simpler way to calculate the total percentage.
Instead of looping through the records, you can use the DSum() function.
Note that DSum will return Null if there are no records, so you need to wrap it in Nz().
Just for fun, here is your function but written as one single statement:
Function RemainingPercentAvailable() As String
RemainingPercentAvailable = "Remaining available: " & Format(1 - _
Nz(DSum("Percentage", _
"tbl_CustomPercent", _
"Tier1 = " & QString(cmbImport_T1) & _
" AND [Month] = " & QString(cmbMonth))) _
, "0.000%")
End Function
I don't recommend building a temporary parameterized query in VBA, because it makes the code too complicated. And slower. I prefer to build "pure" SQL that will run directly in the db engine without any callbacks to Access. I'm assuming that your function is defined in the frmEntry form, and that cmbImport_T1 and cmbMonth are string fields. If they are numeric, omit qString().
Here is my version of your function. It handles the empty-recordset case correctly.
Function RemainingPercentAvailable() As String
Dim CurrentTotal As Double, q As String
q = "SELECT Percentage" & _
" FROM tbl_CustomPercent" & _
" WHERE Tier1 = " & QString(cmbImport_T1) & _
" AND [Month] = " & QString(cmbMonth)
CurrentTotal = 0
With CurrentDb.OpenRecordset(q)
While Not .EOF
CurrentTotal = CurrentTotal + .Fields("Percentage")
.MoveNext
Wend
End With
RemainingPercentAvailable = "Remaining available: " & _
Format(1 - CurrentTotal, "0.000%")
End Function
' Return string S quoted, with quotes escaped, for building SQL.
Public Function QString(ByVal S As String) As String
QString = "'" & Replace(S, "'", "''") & "'"
End Function
So I am doing a query that when you click a button it takes a record set with an unassigned field of data and copies that recordset into the same table with a new "assigned version".
I want my database to be able to make different/multiple "assigned versions" from the original unassigned set, and this works great when i create the first assigned set, but when i try to create a new assigned set it goes into a loop that seems completely random, it could create new entries from 10-1000 and i dont know what is causing this.
Sorry if this was confusing, looking at the code will probably help more
thanks!
Dim rs1 As DAO.Recordset
Dim unionquery As String
Dim CURRENT_SOFTWARE_VERSION As String
CURRENT_SOFTWARE_VERSION = Me.Parent.[Software Version].Value
initialquery = "select [Test Script] , [PROC_CHECK_ID], [Software Version] from (FORMAL_CERT_PROCEDURE_TEST_SCRIPTS inner join FORMAL_CERT_PROCEDURE_CHECK on FORMAL_CERT_PROCEDURE_TEST_SCRIPTS.TEST_CASE_ID = FORMAL_CERT_PROCEDURE_CHECK.TEST_CASE_ID) inner join FORMAL_CERT_SOFTWARE_VERSION on FORMAL_CERT_PROCEDURE_TEST_SCRIPTS.TEST_CASE_ID = FORMAL_CERT_SOFTWARE_VERSION.TEST_CASE_ID where PROC_CHECK_ID=" & Me.PROC_CHECK_ID & " AND [Software Version]=""" & CURRENT_SOFTWARE_VERSION & """ "
Set rs1 = CurrentDb.OpenRecordset(initialquery, dbOpenForwardOnly)
Do Until rs1.EOF = True
Dim rs2 As DAO.Recordset
Set rs2 = CurrentDb.OpenRecordset( _
"SELECT * FROM FORMAL_CERT_PROCEDURE_TEST_SCRIPTS", _
dbOpenDynaset)
rs2.AddNew
rs2![Test Script] = rs1![Test Script]
rs2![PROC_CHECK_ID_FK] = rs1!PROC_CHECK_ID
rs2![Software_Version] = rs1![Software Version].Value
rs2![TEST_CASE_ID] = Me.TEST_CASE_ID
rs2.Update
rs2.Close
Set rs2 = Nothing
rs1.MoveNext
Loop
Oh dear.
If you add records into the table you are currently iterating through, you will have problems in reaching EOF, since you are not only iterating through what you started with, but also the new records that you just added.
The solution is to separate the loop and the insert into discrete steps: loop through and save the values you want to insert, then insert after the loop is finished.
Assuming the values are string, numeric, string, numeric:
Set rs1 = CurrentDb.OpenRecordset(initialquery, dbOpenForwardOnly)
dim strQuery() as String
dim intCounter as Long
dim recordCount as Long
intCounter = 0
rs1.MoveLast
recordCount = rs1.RecordCount
Redim strQuery(0 to recordCount)
rs1.MoveFirst
strQuery(0) = "INSERT INTO FORMAL_CERT_PROCEDURE_TEST_SCRIPTS ([Test Script],[PROC_CHECK_ID_FK],[Software_Version],[TEST_CASE_ID]) VALUES "
Do Until rs1.EOF = True
intCounter = intCounter + 1
strQuery(intCounter) = strQuery(0) & " ('" & rs1![Test Script] & "'," & _
& rs1!PROC_CHECK_ID & "," & _
& "'" & rs1![Software Version].Value & "'," & _
& Me.TEST_CASE_ID & ")"
rs1.MoveNext
Loop
For intCounter = 0 To recordCount
CurrentDb.Execute(strQuery(intCounter))
Next
This will avoid the issue of the EOF pointer moving further away as you insert.
Edit: I forgot you can't do multiple inserts with default DBA, I changed the code to reflect that.
I have very little experience with Access Databases however I have wrote a similar VBA macro in excel. I am trying to copy rows from one .mdb file into the exact same table on a different .mdb file. However I would like it to only import it if it does not already exsist. Could someone please advise me the best way to go about this and maybe some code I can use and modify? I have already looked around stack overflow and can't seem to find any examples that work.
There are 8 different tables and inside these a few hundred rows. with maybe 5-20 columns.
If the script could be made inside VBS this would be ideal, as it would allow me to run the updates without loading into access.
Thanks for any help or advice,
Simon
EDIT -
Zev's answer seems to do the job however I am getting this error, also the MDB inside site2 is the one I am copying from and putting it into site1
Error: Expected end of statement
Code: 800A0401
Line: 17
Char: 13
Code (saved as "update.vbs"):
Dim eng
Set eng = CreateObject("DAO.DBEngine.120")
Set dest = eng.OpenDatabase("C:\Users\simon\Documents\garden games redesign\import script\Site1\ActinicCatalog.mdb")
Sub CopyTable()
Dim rs
Set rs = dest.OpenRecordset("Person")
Dim sWhere
For Each fld In rs.Fields
sWhere = sWhere & " AND " & fld.Name & " <> t1." & fld.Name
Next
sWhere = Mid(sWhere, 6)
Dim sql: sql= _
"INSERT INTO Person " & _
"SELECT * " & _
"FROM Person AS t1 IN ""C:\Users\simon\Documents\garden games redesign\import script\Site2\ActinicCatalog.mdb"" " & _
"WHERE " & sWhere
dest.Execute(sql)
End Sub
Edit for more info:
\Site1\ActinicCatalog.mdb - is destination database
\Site2\ActinicCatalog.mdb - is original database
These databases have about 20 columns
Here is an example to get you started. It copies the content of [Table1] of the current database to [Table1] of a second database.
Option Compare Database
Option Explicit
Sub copyTables()
'Open source database
Dim dSource As Database
Set dSource = CurrentDb
'Open dest database
Dim dDest As Database
Set dDest = DAO.OpenDatabase("C:\Users\Admin\Desktop\DBdest.accdb")
'Open source recordset
Dim rSource As Recordset
Set rSource = dSource.OpenRecordset("Table1", dbOpenForwardOnly)
'Open dest recordset
Dim rDest As Recordset
Set rDest = dDest.OpenRecordset("Table1", dbOpenDynaset)
'Loop through source recordset
While Not rSource.EOF
'Look for record in dest recordset
rDest.FindFirst _
"Field1 = '" & rSource.Fields("Field1") & "' AND " & _
"Field2 = " & rSource.Fields("Field2")
'If not found, copy record - Field1 is text / Field2 is numeric
If rDest.NoMatch Then
rDest.AddNew
rDest.Fields("Field1") = rSource.Fields("Field1")
rDest.Fields("Field2") = rSource.Fields("Field2")
rDest.Update
End If
'Next source record
rSource.MoveNext
Wend
'Close dest recordset
rDest.Close
Set rDest = Nothing
'Close source recordset
rSource.Close
Set rSource = Nothing
'Close dest database
dDest.Close
Set dDest = Nothing
'Close source database
dSource.Close
Set dSource = Nothing
End Sub
I would suggest using an SQL statement if possible. From VBScript using DAO/ACE:
Dim eng
Set eng = CreateObject("DAO.DBEngine.120")
Set dest = eng.OpenDatabase("path\to\destination\database.accdb")
Using ADO:
Dim conn
Set conn = CreateObject("ADODB.Connection")
With conn
.Provider = "Microsoft.ACE.OLEDB.12.0"
.ConnectionString = "Data Source=""path\to\destination\database.accdb"";"
.Open
End With
The SQL statement would be something like this:
INSERT INTO Table1
SELECT *
FROM Table1 AS t1 IN "path\to\source\database.accdb"
WHERE Table1.Field1 <> t1.Field1
and executed like this:
Dim sql = _
"INSERT INTO Table1 " & _
"SELECT * " & _
"FROM Table1 AS t1 IN "path\to\source\database.accdb" " & _
"WHERE Table1.Field1 <> t1.Field1"
'Using DAO or ADO
dest.Execute sql
Considering that each table has a variable number of columns, you might have to generate the WHERE expression dynamically:
Sub CopyTable(tablename)
Dim rs
Set rs = dest.OpenRecordset(tablename)
'if using ADO:
'Set rs = conn.Execute(tablename)
Dim sWhere
For Each fld In rs.Fields
sWhere = sWhere & " AND " & fld.Name & " <> t1." & fld.Name
Next
sWhere = Mid(sWhere, 6)
Dim sql
sql = _
"INSERT INTO " & tablename & " " & _
"SELECT * " & _
"FROM " & tablename & " AS t1 IN ""path\to\source\database.accdb"" " & _
"WHERE " & sWhere
dest.Execute(sql)
End Sub
Update
If you are only using one column to determine whether the record exists, the SQL statement should look like this:
INSERT INTO Table1
SELECT *
FROM Table1 AS t1 IN "path\to\source\database.accdb"
LEFT JOIN Table1 ON t1.FirstField = Table1.FirstField
WHERE Table1.FirstField IS NULL
and CopyTable like this:
Sub CopyTable(tablename)
Dim rs
Set rs = dest.OpenRecordset(tablename)
'if using ADO:
'Set rs = conn.Execute(tablename)
Dim field0Name
field0Name=rs.Fields(0).Name
Dim sql
sql = _
"INSERT INTO " & tablename & " " & _
"SELECT * " & _
"FROM " & tablename & " AS t1 IN ""path\to\source\database.accdb"" " & _
"LEFT JOIN " & tablename & " ON t1." & field0Name & "=" & tablename & "." & field0Name & " " & _
"WHERE " & tablename & "." & field0Name & " IS NULL"
dest.Execute(sql)
End Sub