Access VBA: Concatenate dynamic columns and execute in loop - sql

My question is related to the following two Access tables, named Table_1 and Table_2.
The following codes aims to update [Table_2.CombinedField] column, by concatenating two other columns of the same table. One of the two columns must be [Table_2.BookName], the other column is defined in Table_1.
For example, as you can see in Table_1, novel BookType should use Author to concatenate with BookName, research BookType should use PublishYear etc. That means which column to be used for concatenate in Table_2 is based on Table_1.
Ideally, the target result for following codes should be:
CombinedField
tom - titleA
john - titleB
2010 - titleC
2011 - titleD
company5 - titleE
However, as you see Table_2.CombinedField in above Table_2 screenshot. The code only used the first row of Table_1 (Author) and applies to all rows of Table_2.
Function CombineVariableFields_NoLoop()
On Error Resume Next
Dim ws As Workspace
Dim strSQL As String
Dim fieldname As String
fieldname = DLookup("[SelectCombineField]", "Table_1")
Set ws = DBEngine.Workspaces(0)
Set db = CurrentDb()
On Error GoTo Proc_Err
ws.BeginTrans
strSQL = "UPDATE Table_2 INNER JOIN Table_1 ON Table_2.BookType = Table_1.BookType SET Table_2.CombinedField = [Table_2]![" & fieldname & "] & ' - ' & [Table_2]![BookName]"
db.Execute strSQL, dbFailOnError
ws.CommitTrans
Proc_Exit:
Set ws = Nothing
Exit Function
Proc_Err:
ws.Rollback
MsgBox "Error updating: " & Err.Description
Resume Proc_Exit
End Function
My question
I guess I should use something like loop. However, I don't really know how should I apply loop to the codes in this scenario. (sorry i am new to VBA). Below coding is something by my guess only, appreciate if someone can help to point out what exact codes should be in order to generate my target result for Table_2.CombinedField. Thanks a lot.
The following codes is only my guess
Function CombineVariableFields_Loop()
On Error Resume Next
Dim ws As Workspace
Dim strSQL As String
Dim fieldname As String
Set ws = DBEngine.Workspaces(0)
Set db = CurrentDb()
On Error GoTo Proc_Err
ws.BeginTrans
Set rst = db.OpenRecordset("Select distinct SelectCombineField FROM Table_1", dbOpenDynaset)
With rst
Do While Not .EOF
fieldname = DLookup("[SelectCombineField]", "Table_1", "BookType = " & DLookup("BookType", "Table_2"))
strSQL = "UPDATE Table_2 INNER JOIN Table_1 ON Table_2.BookType = Table_1.BookType SET Table_2.CombinedField = [Table_2]![" & fieldname & "] & ' - ' & [Table_2]![BookName]"
db.Execute strSQL, dbFailOnError
.MoveNext
Loop
End With
ws.CommitTrans
Proc_Exit:
Set ws = Nothing
Exit Function
Proc_Err:
ws.Rollback
MsgBox "Error updating: " & Err.Description
Resume Proc_Exit
End Function

try this:
Set rst = db.OpenRecordset("Select SelectCombineField, BookType FROM Table_1 Where BookType In(Select Distinct BookType From Table_2)", dbOpenDynaset)
With rst
Do While Not .EOF
strSQL = "UPDATE Table_2 SET Table_2.CombinedField = [Table_2].[" & !SelectCombineField & "] & ' - ' & [Table_2].[BookName] Where BookType = '" & !BookType & "'"
db.Execute strSQL, dbFailOnError
.MoveNext
Loop
End With

This may not be the BEST way, but it should work:
"UPDATE Table_2 a INNER JOIN Table_1 b ON a.BookType = b.BookType" _
& "SET Table_2.CombinedField = " _
& "Iif(b.[SelectCombinedField = 'Author', a.[Author], " _
& " Iif(b.[SelectCombinedField = 'PublishYear', a.[PublishYear], " _
& "a.[Publisher])) & ' - ' & a.[BookName]"

Related

ms access vba "Run-time error '3061'. Too few parameters. Expected 7" when querying access query

I google a lot and read all post here related to this issue but found nothing that could give an explanation or help me to resolve this issue.
Following here, a function which work great when the "TableName" parameter is a base table but raise error when it is an ms access view (query). I found nothing yet that could explain this issue as many access query already refer to such views (queries) without issues.
Function DBDistinctCount(FieldName As String, tableName As String) As Long
Dim rs As Recordset, curDb As Database, strSql As String
On Error GoTo ERR_Handling
Set curDb = CurrentDb
'strSql = "SELECT COUNT(PR.[" & FieldName & "]) AS CNT FROM (SELECT [" & FieldName & "] FROM " & TableName & " GROUP BY [" & FieldName & "]) AS PR;"
strSql = "SELECT COUNT(PR." & FieldName & ") AS CNT FROM (SELECT " & FieldName & " FROM " & tableName & " GROUP BY " & FieldName & ") AS PR;"
'strSql = "SELECT COUNT([" & FieldName & "]) AS CNT FROM (SELECT [" & FieldName & "] FROM [" & TableName & "] GROUP BY [" & FieldName & "]);"
'Debug.Print result: SELECT COUNT(PR.ID_Projet) AS CNT FROM (SELECT ID_Projet FROM R_CompilationProjet GROUP BY ID_Projet) AS PR
' Dim qdf As DAO.QueryDef
' Set qdf = curDb.CreateQueryDef(vbNullString, strSql)
Set rs = curDb.OpenRecordset(strSql)
' Set rs = qdf.OpenRecordset(dbOpenSnapshot)
DBDistinctCount = Nz(rs.Fields("CNT"), 0)
ERR_Handling:
If Err.Number <> 0 Then
Dim mess As String
mess = "Erreur vba " & Err.Number & " : " & Err.Description
On Error Resume Next
Call DBHelper.AddLog(mess, "DBDistinctCount")
End If
If Not rs Is Nothing Then rs.Close
Set rs = Nothing
If Not curDb Is Nothing Then curDb.Close
Set curDb= Nothing
End Function
As you can see, I messed up the function a bit in order to find out what could be wrong. I even tried to use a querydef with the same result. I should mention that I've tried to put the resulting sql string itself inside an access query to see exactly the expected result when I ran the query. Any advice would be greatly appreciated.

Difficulty using SELECT statement in VBA

I'm trying to set command buttons to enter data into a table. If the record already exists I want the button to update it, and if it does not the button needs to create it. Here's a sample of what the table looks like
ID scenarioID reductionID Impact Variable Variable Impact
1 1 1 Safety 4
2 1 1 Environmental 2
3 1 1 Financial 1
In order to accurately locate records, it needs to search for the specific impact variable paired with the scenarioID. I'm trying to use a select statement, but DoCmd.RunSQL doesn't work for select statements, and I'm not sure how else to do it.
Here's the code. I left DoCmd.SQL in front of the select statement for lack of anything else to place there for now.
Private Sub Var1R1_Click() 'Stores appropriate values in tImpact upon click
'Declaring database and setting recordset
Dim db As Database
Dim rs As Recordset
Set db = CurrentDb
Set rs = db.OpenRecordset("tImpact")
'Declaring Variable as Scenario Choice combobox value
Dim Sc As Integer
Sc = Schoice.Value
'Stores impact variable
Dim impvar1 As String
'Desired impact variable for column 1
impvar1 = DLookup("impactVariable", "tImpactVars", "ID = 1")
DoCmd.RunSQL "SELECT * FROM tImpact WHERE [Impact Variable] = " & impvar1 & " AND scenarioID = " & Sc
If rs.EOF Then
DoCmd.RunSQL "INSERT INTO tImpact(scenarioID, [Impact Variable], [Variable Impact])" & "VALUES (" & Sc & ", " & impvar1 & ", 1)"
MsgBox "Record Added", vbOKOnly
Else
db.Execute "UPDATE tImpact SET [Variable Impact] = 1 WHERE [Impact Variable] = " & impvar1 & " AND scenarioID = " & Sc
MsgBox "Record Updated", vbOKOnly
End If
End Sub
If anyone can tell me how to get that SELECT statement to run, or another way of doing this, that would be great.
Any help is greatly appreciated!
You can use a recordset. In this case a recordset is better, since you only execute the SQL one time, if it returns a record, you "edit" and if not then you "add" with the SAME reocrdset. This approach is FAR less code, and the values you set into the reocrdset does not require messy quotes or delimiters etc.
eg:
scenaridID = 1 ' set this to any number
impvar1 = "Safety" ' set this to any string
updateTo = "Financial"
strSQL = "select * from tImpact where [Impact Variable] = '" & impvar1 & "'" & _
" AND scenaridID = " & scenaridID
Set rst = CurrentDb.OpenRecordset(strSQL)
With rst
If .RecordCount = 0 Then
' add the reocrd
.AddNew
Else
.Edit
End If
!scenaridID = scenarid
![Impact Variable] = impvar1
![Variable Impact] = 1
.Update
End With
rst.Close
So you can use the same code for the update and the edit. It just a question if you add or edit.
Use OpenRecordset and retrieve the ID for the record if it exists.
Private Sub Command0_Click()
Dim aLookup(1 To 3) As String
Dim aAction(1 To 3) As String
Dim rs As Recordset
Dim db As Database
'Replace these two constants with where you get the information on your form
Const sIMPVAR As String = "Financial"
Const lSCENID As Long = 1
'Build the SQL to find the ID if it exists
aLookup(1) = "SELECT ID FROM tImpact"
aLookup(2) = "WHERE ScenarioID = " & lSCENID
aLookup(3) = "AND ImpactVariable = """ & sIMPVAR & """"
'Run the sql to find the id
Set db = CurrentDb
Set rs = db.OpenRecordset(Join(aLookup, Space(1)))
If rs.BOF And rs.EOF Then 'it doesn't exist, so build the insert statement
aAction(1) = "INSERT INTO tImpact"
aAction(2) = "(ScenarioID, ImpactVariable, VariableImpact)"
aAction(3) = "VALUES (" & lSCENID & ", '" & sIMPVAR & "', 1)"
Else 'it does exist, so build the update statement
aAction(1) = "UPDATE tImpact"
aAction(2) = "SET VariableImpact = 1"
aAction(3) = "WHERE ID = " & rs.Fields(0).Value
End If
'Run the action query
db.Execute Join(aAction, Space(1))
rs.Close
Set rs = Nothing
Set db = Nothing
End Sub

VBA Access Inserting from One DAO.Recordset into Another

Within the following code
Dim sqlStr As String
Dim OrgID As Long
Dim wrk As DAO.Workspace
Dim db As DAO.Database
Dim orgRS As DAO.Recordset
Dim staffRS As DAO.Recordset
Set wrk = DBEngine.Workspaces(0)
Set db = CurrentDb
On Error GoTo trans_Err
InsertOrganizationIntoTemp
'if staff fields are not blank Insert Staff into Temp
If (addStaffClickCheck = True) Then
staffNumber = staffNumber + 1
InsertStaffIntoTemp staffNumber
End If
wrk.BeginTrans
sqlStr = "INSERT INTO tblOrganization(OrganizationName, Affiliate, " _
& " UnionCouncil, DateJoined, OrganizationPhoneNumber, OrganizationFaxNumber, " _
& " MembershipGroup, TradeGroup, URL) " _
& " SELECT OrganizationName, Affiliate, CouncilUnion, DateJoined, OrganizationPhone, " _
& " OrganizationFax, MemberGroup, Trade, OrganizationWebsite " _
& " FROM tblTempOrganization;"
db.Execute sqlStr
OrgID = db.OpenRecordset("SELECT ##IDENTITY")(0)
sqlStr = "INSERT INTO tblAddresses(StreetName, CityName, StateName, " _
& " ZipCode, LocationID, OrganizationID) " _
& " SELECT OrganizationAddress, OrganizationCity, OrganizationState, " _
& " OrganizationZIP, OrganizationLocationType, '" & OrgID & "' " _
& " FROM tblTempOrganization;"
db.Execute sqlStr
'pull all staff and phones into two recordsets
'loop through staff and for each staff member add associated phone information
'Recordsets for temporary staff tables
Dim staffTempInfoRS As DAO.Recordset
Dim phoneTempInfoRS As DAO.Recordset
Set staffTempInfoRS = db.OpenRecordset("SELECT * FROM tblTempStaff", dbOpenDynaset)
'Recordsets for permanant staff tables
Dim StaffAddressID As Long
Dim StaffID As Long
'Check to see if the recordset actually contains rows
If Not (staffTempInfoRS.EOF And staffTempInfoRS.BOF) Then
staffTempInfoRS.MoveFirst 'Unnecessary in this case, but still a good habit
Do Until staffTempInfoRS.EOF = True
'address information
Dim staffAddressDBRS As DAO.Recordset
Set staffAddressDBRS = db.OpenRecordset("tblStaffAddresses", dbOpenDynaset)
With staffAddressDBRS
.AddNew
.Fields("StaffStreet") = staffTempInfoRS.Fields("StaffStreet")
.Fields("StaffCity") = staffTempInfoRS.Fields("StaffCity")
.Fields("StaffState") = staffTempInfoRS.Fields("StaffState")
.Fields("StaffZip") = staffTempInfoRS.Fields("StaffZip")
.Update
End With
StaffAddressID = staffAddressDBRS.LastModified
staffAddressDBRS.Close
Set staffAddressDBRS = Nothing
'staff information
Dim staffInfoDBRS As DAO.Recordset
Set staffInfoDBRS = db.OpenRecordset("tblStaff", dbOpenDynaset)
With staffInfoDBRS
.AddNew
.Fields("StaffFirstName") = staffTempInfoRS.Fields("StaffFirstName")
.Fields("StaffLastName") = staffTempInfoRS.Fields("StaffLastName")
.Fields("Email") = staffTempInfoRS.Fields("Email")
.Fields("StaffAddressID") = StaffAddressID
.Fields("OrganizationID") = OrgID
.Fields("Position") = staffTempInfoRS.Fields("StaffPosition")
.Update
End With
Dim currPos As Long
currPos = staffTempInfoRS.Fields("StaffNumber")
StaffID = staffInfoDBRS.LastModified
staffInfoDBRS.Close
Set staffInfoDBRS = Nothing
'staff phone information
Set phoneTempInfoRS = db.OpenRecordset("SELECT * FROM tblTempPhones WHERE StaffNumber = " & currPos & ";")
If Not (phoneTempInfoRS.EOF And phoneTempInfoRS.BOF) Then
phoneTempInfoRS.MoveFirst 'Unnecessary in this case, but still a good habit
Do Until phoneTempInfoRS.EOF = True
Dim phoneInfoDBRS As DAO.Recordset
Set phoneInfoDBRS = db.OpenRecordset("tblStaffPhoneNumbers", dbOpenDynaset)
With phoneInfoDBRS
.AddNew
.Fields("PhoneNumber") = phoneTempInfoRS.Fields("StaffPhoneNumber")
.Fields("PhoneTypeID") = phoneTempInfoRS.Fields("PhoneType")
.Fields("StaffID") = StaffID
.Update
End With
phoneTempInfoRS.MoveNext
Loop
Else
Resume Next
End If
MsgBox "Finished looping through phone records."
phoneTempInfoRS.Close
Set phoneTempInfoRS = Nothing
'Move to the next record. Don't ever forget to do this.
staffTempInfoRS.MoveNext
Loop
Else
MsgBox "There are no records in the staff recordset."
End If
MsgBox "Finished looping through staff records."
staffTempInfoRS.Close 'Close the recordset
Set staffTempInfoRS = Nothing 'Clean up
wrk.CommitTrans
trans_Exit:
'Clean up
wrk.Close
Set db = Nothing
Set wrk = Nothing
Exit Function
trans_Err:
'Roll back the transaction
MsgBox "Whoops! We got errors"
wrk.Rollback
Resume trans_Exit
When I step through this I get an error in this line:
.Fields("StaffStreet") = staffTempInfoRS.Fields("StaffStreet")
That says:
Item not found in collection.
However, this is the exact field name that is in the table that the recordset is set to open.
30 hours later it seems I suffered a rookie mistake and in-spite of repeated checking missed that the field names were indeed misspelled.
I also set the recordsets outside of the loops and I unset them all in the exit snippet...

Selecting Random Record and Marking Record As Being Used

So, the following selects a random team to be used. Once selected, I mark this team being as used as an X in the Used field with a update query later on. For the most it works, but after running this a handful of times, I start to get duplicated teams, even with them being marked as X.
What else am I missing to accomplish this?
Here is the SQL statement:
SELECT TOP 1 RandomTeams.[Team Name], RandomTeams.Used
FROM RandomTeams
WHERE (((RandomTeams.Used) Is Null))
ORDER BY Rnd(TeamID);
Here's how I'm handling the updates to mark a team as being used, which is working as expected, I have no issues here when marking with an X:
Text214.Text contains the team name that is being used
strTeam = (Chr(34) + Text214.Text + (Chr(34)))
strSQLUpdateTeams = "UPDATE RandomTeams SET Used = 'X' WHERE [Team Name] = " & strTeam
DoCmd.SetWarnings (WarningsOff)
DoCmd.RunSQL strSQLUpdateTeams
As a test, how about just throwing the following code into a module, then execute it and see what happens. BTW, how are you resetting [Used]?
Sub Test_Teams()
Dim strSQL As String
Dim strTeam As String
Dim strSQLUpdateTeams As String
Dim dbs As DAO.Database
Dim rs As DAO.recordSet
Set dbs = CurrentDb
If MsgBox("Do you want to reset all 'Used' flags?", vbYesNo, "Reset?") = vbYes Then
strSQL = "update RandomTeams set [Used] = null;"
dbs.Execute strSQL
End If
MyLoop:
strSQL = "SELECT TOP 1 RandomTeams.[Team Name], RandomTeams.Used " & _
"FROM RandomTeams " & _
"WHERE (((RandomTeams.Used) Is Null)) " & _
"ORDER BY Rnd(TeamID);"
Set rs = dbs.OpenRecordset(strSQL)
If Not rs.EOF Then
strTeam = rs![Team Name]
Debug.Print "Found Team: " & strTeam
Else
MsgBox "EOF! No more teams."
rs.Close
Set rs = Nothing
dbs.Close
Set dbs = Nothing
Exit Sub
End If
strTeam = (Chr(34) + rs![Team Name] + (Chr(34)))
rs.Close
Set rs = Nothing
strSQLUpdateTeams = "UPDATE RandomTeams SET [Used] = 'X' WHERE [Team Name] = " & strTeam
Debug.Print strSQLUpdateTeams
'DoCmd.SetWarnings (WarningsOff)
'DoCmd.RunSQL strSQLUpdateTeams
dbs.Execute strSQLUpdateTeams
If dbs.RecordsAffected <> 1 Then
MsgBox "Whoa! Not good! Update failed!"
End If
GoTo MyLoop
End Sub

Field name confusion

rs2.FindFirst "[aniin] ='" & strTemp & "'"
aniin being an alias from the SQL within the function.
also tried ...
rs2.FindFirst (niin = newdata)
is my attempt to isolate the field name niin from the record value in the form from the one in the strSQL2. All my attempts have failed. I am trying to make sure that what the user typed in does match the list from the SQL string.
Private Function IsPartOfAEL(newdata) As Boolean
On Error GoTo ErrTrap
Dim db2 As DAO.Database
Dim rs2 As DAO.Recordset
Dim strTemp As String
strSQL2 = "SELECT tbl_ael_parts.master_ael_id, tbl_master_niin.niin as aniin " & vbCrLf & _
"FROM tbl_master_niin INNER JOIN tbl_ael_parts ON tbl_master_niin.master_niin_id = tbl_ael_parts.master_niin_id " & vbCrLf & _
"WHERE (((tbl_ael_parts.master_ael_id)= " & Forms!frm_qry_niin_local!master_ael_id & "));"
Set db2 = CurrentDb
Set rs2 = db2.OpenRecordset(strSQL2)
strTemp = newdata
If rs2.RecordCount <> 0 Then
rs2.FindFirst "[aniin] ='" & strTemp & "'"
If rs2.NoMatch Then
IsPartOfAEL = False
Else
IsPartOfAEL = True
End If
Else
MsgBox "Query Returned Zero Records", vbCritical
Exit Function
End If
rs.Close
Set rs2 = Nothing
Set db2 = Nothing
ExitHere:
Exit Function
ErrTrap:
MsgBox Err.description
Resume ExitHere
End Function
First: You should never include a constant like vbCrLf when building a query string. The query parser doesn't care if there's a linefeed, and in fact this can sometimes cause issues.
Your code seems to do nothing more that verify whether the value in newdata exists in the tbl_ael_parts and is associated with the value master_ael_id value currently showing on frm_qry_niin_local. If so, then just use DCount, or use this for your query:
strSQL2 = "SELECT tbl_ael_parts.master_ael_id INNER JOIN tbl_ael_parts ON
tbl_master_niin.master_niin_id = tbl_ael_parts.master_niin_id WHERE (((tbl_ael_parts.master_ael_id)=
" & Forms!frm_qry_niin_local!master_ael_id & ") AND niin=" & newdata & ");"
Dim rst As DAO.Recordset
Set rst = currentdb.OPenrecordset(strsql2)
If (rst.EOF and rst.BOF) Then
' no records returned
Else
' records found
End If
If niin is a Text field:
strSQL2 = "SELECT tbl_ael_parts.master_ael_id INNER JOIN tbl_ael_parts ON
tbl_master_niin.master_niin_id = tbl_ael_parts.master_niin_id WHERE (((tbl_ael_parts.master_ael_id)=
" & Forms!frm_qry_niin_local!master_ael_id & ") AND (niin='" & newdata & "'));"
If both niin and master_ael_id are Text fields:
strSQL2 = "SELECT tbl_ael_parts.master_ael_id INNER JOIN tbl_ael_parts ON
tbl_master_niin.master_niin_id = tbl_ael_parts.master_niin_id WHERE (((tbl_ael_parts.master_ael_id)=
'" & Forms!frm_qry_niin_local!master_ael_id & "') AND (niin='" & newdata & "'));"