VBA Query that pulls data from both excel table and SQL database - sql

I have been working on this for quite a while and I can't seem to figure it out. I am trying to create a query in excel that combines information from an excel table and database. I can do each of them separately no problem.
Here is the VBA code for the excel query:
Sub ExcelQuery()
'
Range("Table_Query_from_Excel_Files5[[#Headers],[Customer:]]").Select
With Selection.ListObject.QueryTable
.Connection = Array(Array("ODBC;DSN=Excel Files;DBQ=Z:\OEM Office\Trevor Weinrich\Projects\BOM Template 2.0\BOM template 2017-08-16 1.xlsm;DefaultDir=Z:\OEM Office\Trevor Weinrich\Projects\BOM Template 2.0;DriverId=1046;MaxBufferSize=2048;PageTimeout=5;"))
.CommandText = Array( _
"SELECT `BOM$`.`Customer:`" & Chr(13) & "" & Chr(10) & "FROM `BOM$` `BOM$`" & Chr(13) & "" & Chr(10) & "WHERE (`BOM$`.`Customer:` Is Not Null)" _
)
.Refresh BackgroundQuery:=False
End With
'
End Sub
And here is the VBA code for the database query:
Sub DatabaseQuery()
'
With Selection.ListObject.QueryTable
.Connection = _
"ODBC;DSN=OEM;Description=OEM;UID=trevor.weinrich;Trusted_Connection=Yes;APP=Microsoft Office 2016;WSID=DFP-OEM-0913-A;DATABASE=OEM"
.CommandText = Array( _
"SELECT DISTINCT p21_view_item_uom.item_id, p21_view_item_uom.unit_of_measure, p21_view_item_uom.purchasing_unit" & Chr(13) & "" & Chr(10) & "FROM OEM.dbo.p21_view_item_uom p21_view_item_uom" & Chr(13) & "" & Chr(10) & "WHERE (p21_view_item_uom.delete_flag=" _
, "'N')" & Chr(13) & "" & Chr(10) & "ORDER BY p21_view_item_uom.item_id")
.Refresh BackgroundQuery:=False
End With
'
End Sub
I want to join these together because there are about 140,000 line items in the database query, and I only care about the the instances where the "item_id" field from the database matches up with the "Customer:" field.
I just can't figure out how to join the two of them. I would greatly appreciate the help.
Here is the code where I am just trying to pull in a variable that gives me the error after 165 characters:
Sub Update_Item_Tables()
'
' UOM_Update Macro
'
Dim Items As String
Items = Sheets("UOM").Range("K1").Value
Sheets("UOM").Visible = True
Sheets("UOM").Select
Range("Table_Query_from_OEM[[#Headers],[item_id]]").Select
With Selection.ListObject.QueryTable
.Connection = _
"ODBC;DSN=OEM;Description=OEM;UID=trevor.weinrich;Trusted_Connection=Yes;APP=Microsoft Office 2016;WSID=DFP-OEM-0913-A;DATABASE=OEM"
.CommandText = Array( _
"SELECT DISTINCT p21_view_item_uom.item_id, p21_view_item_uom.unit_of_measure, p21_view_item_uom.purchasing_unit" & Chr(13) & "" & Chr(10) & _
"FROM OEM.dbo.p21_view_item_uom p21_view_item_uom" & Chr(13) & "" & Chr(10) & _
"WHERE (p21_view_item_uom.item_id In (" _
, _
"" & Items & ")) AND (p21_view_item_uom.delete_flag='N')" & Chr(13) & "" & Chr(10) & _
"ORDER BY p21_view_item_uom.purchasing_unit DESC" _
)
.Refresh BackgroundQuery:=False
End With
End Sub

Yikes... some of that code is... let's call it unique. But I won't ask questions, lets just deal with the problem you are asking about for now.
The goal is to check your long Items string so you only need one Sql command to grab your data. But the string needs to be in chunks less than 165 characters.
As I mentioned before, this is a sloppy way to do it, but if they have you locked out of your SQL database for development purposes, this approach will work:
You can split your Item string into multiple strings each less than 165 characters. Then you can use OR statements in your SQL to use the IN function with each Item string that has data.
First, in your VBA, you need to:
'Delcare your Variables
Dim SQLText As String
Dim Items2 As String
Dim Items3 As String
Dim boolItemFlag2 As Boolean
Dim boolItemFlag3 As Boolean
Dim i As Integer
' Initialize Variables
boolItemFlag2 = False
boolItemFlag3 = False
Then you can do your processing to build your 'Items' string. I'm assuming it's comma delimited something like this:
Items = "'123456','234567'"
After it's built you can:
' Get your Items list and check for length over 164
If Not IsNull(Len(Items)) Then
If Len(Items) > 164 Then
boolItemFlag2 = True
i = 0
' Find last comma delimiter before the cut off of 164
While (Mid(Items, 164 - i, 1) <> ",")
i = i + 1
Wend
' Set Items2 to everything after the last comma (before pos 164). Then Left Trim away possible spaces.
Items2 = LTrim(Mid(Items, 164 - i + 1, Len(Items) - 164 + i))
' Reset Items to everything up to that comma we found but not including it.
Items = Left(Items, 164 - i - 1)
' Your Item list is now split into 2 different string variables
End If
End If
If Not IsNull(Len(Items2)) Then
If Len(Items2) > 164 Then
boolItemFlag3 = True
' Use the same logic above to split Items2 into the Items3 variable.
' You may need to duplicate again for Items4 etc if your Item List is huge.
' If you'd need beyond Items5, I'd probably go back to the two query approach.
' But it's your choice.
End If
End If
Then, after you have your Item lists split into variables that are each small enough, you can build a unique SQL string (called SQLText) and insert OR statements to include items strings that have data:
' Build your SQL String Here (after you have determined how many item strings you have)
SQLText = "SELECT DISTINCT p21_view_item_uom.item_id, p21_view_item_uom.unit_of_measure, p21_view_item_uom.purchasing_unit" & Chr(13) & "" & Chr(10) & _
"FROM OEM.dbo.p21_view_item_uom p21_view_item_uom" & Chr(13) & "" & Chr(10) & _
"WHERE (p21_view_item_uom.item_id In (" _
& _ ' <-- I changed your comma into an & because I have no clue...
"" & Items & ")"
If boolItemFlag2 Then ' Add this OR statement if Items2 has data
SQLText = SQLText & " OR p21_view_item_uom.item_id In (" & Items2 & ")"
End If
If boolItemFlag3 Then ' Add this OR statement if Items3 has data
SQLText = SQLText & " OR p21_view_item_uom.item_id In (" & Items3 & ")"
End If
' Add more OR statements if you have Item lists beyond 3.
' Now tack on your remaining SQL code:
SQLText = SQLText & ") AND (p21_view_item_uom.delete_flag='N')" & Chr(13) & "" & Chr(10) & _
"ORDER BY p21_view_item_uom.purchasing_unit DESC"
Now the String SQLText will have your complete SQL code that you want to send to the server. You can execute this message box if you want to verify the string is correct:
MsgBox SQLText
You need to make one more modification. When you call the database, you need to put the variable SQLText into the commandtext array (in place of your old code):
' Now you can put your SQLText variable into your ODBC call:
With Selection.ListObject.QueryTable
.Connection = _
"ODBC;DSN=OEM;Description=OEM;UID=trevor.weinrich;Trusted_Connection=Yes;APP=Microsoft Office 2016;WSID=DFP-OEM-0913-A;DATABASE=OEM"
.CommandText = Array(SQLText)
.Refresh BackgroundQuery:=False
End With
Hope that helps :)

Run your Excel query.
Loop through the result set and build a list of OR conditions, such as item_id = 'ABC' Or item_id = 'PQR' and so on.
Instead of using an IN condition, use the (huge) list of OR conditions in a single large string.
Concatenate this list of OR conditions with rest of your query as Criterion for your database query.
While the length of an IN phrase may have a restriction, the length of the complete SQL statement may be higher.
Of course, if the no. of customers going to appear in your query is too large, this will have unexpected issues depending on how large it is.

Related

Recordset Not Updating Table with Changed and/or Correct Values

The codes purpose is to 'build' the correct name style for each record in a CDSal and FormatName field. I have a group of tables (all linked) with individuals Full Name(NewName), Salutation, First, Middle and Last Name, as well as Client defaults for what to do with those names (!NewName, !First, !AA, etc.).
The Recordset is pulled from a query in the database that brings some necessary fields together from 2 different tables. From Access I can open the query, make any changes needed to any of the fields, save the record and see the changes reflected in the underlying tables. When I run the following code, the Debug.Print's produce the expected outcomes but nothing is permanently saved to the tables. The code never errors (which might be part of the problem) and for Case "!AA" both CDSal and FormatName fields are filled with !NewName when Debug.Print again shows the expected outcome. Case "!AA" is the only instance where anything is actually changed on the tables.
I have attempted everything that I could find on the Internet to troubleshoot this error as well as multiple different configurations to get something to "stick". Hopefully it is a simple answer, let me know what you all think.
Private Sub Form_Load()
On Error GoTo Form_Load_Err
'_ SetUp Variables _'
Dim strQry As String, strSQL As String, strName As String
Dim rstName As DAO.Recordset
'_ Declare Variables _'
strQry = "MyQueryName"
Set rstName = CurrentDb.OpenRecordset(strQry, dbOpenDynaset)
'_ Begin Code _'
With rstName
If Not (.EOF And .BOF) Then .MoveFirst
Do Until .EOF = True
'Update CDSal with correct Naming Information
Debug.Print !NewName
.Edit
Select Case !CDSal_Client
Case "NewName" 'Clients that use NewName for blah
!CDSal = !NewName
Case "First" 'Clients that use First for blah
!CDSal = !First
Case "AA" 'ClientName: CDSal = First, FormatName = NewName(w/o Sal)
!CDSal = !First
If !Sal <> "" Then
!FormatName = !First & " " & !Middle & " " & !Last
Else
!FormatName = !NewName
End If
Case "BB" 'ClientName: Client uses specific breakdown for names
If !Sal <> "" And !Last <> "" Then
!CDSal = !Sal & " " & !Last
!FormatName = !Sal & " " & !Last
ElseIf !First <> "" And !Last <> "" Then
!CDSal = !First & " " & !Last
!FormatName = !First & " " & !Last
ElseIf !First <> "" Then
!CDSal = !First
!FormatName = !First
Else
!CDSal = "Valued Member"
!FormatName = "Valued Member"
End If
Case "CC" 'ClientName: CDSal = NewName(trim " & " if needed) = NewName + AddlName(done on import)
If Right(!NewName, 3) = " & " Then
Replace !NewName, " & ", ""
!CDSal = !NewName
Else
!CDSal = !NewName
End If
End Select
.Update
Debug.Print !CDSal
Debug.Print !FormatName
.MoveNext
Loop
'Removes additional spaces left over from concatenating fields
strSQL = "UPDATE [" & strQry & "] SET [FormatName] = REPLACE(REPLACE(REPLACE([FormatName],' ','<>'),'><',''),'<>',' '), " & _
"[CDSal] = REPLACE(REPLACE(REPLACE([FormatName],' ','<>'),'><',''),'<>',' ');"
CurrentDb.Execute strSQL
End With
'_ Error Handling & CleanUp
Form_Load_ClnUp:
rstName.Close
Set rstName = Nothing
Exit Sub
Form_Load_Err:
MsgBox Err.SOURCE & " : " & Err.Number & vbCr & _
"Error Description : " & Err.Description
GoTo Form_Load_ClnUp
End Sub
MyQueryName SQL
SELECT T_Individual.ID_IndivRecords, T_Individual.NewName, T_Individual.NewName2, T_Individual.CDSal, T_Individual.FormatName, T_Individual.Status_, T_Individual.Sal, T_Individual.First, T_Individual.Middle, T_Individual.Last, T_Clients.ID_Client, T_Clients.CDSal_Client, T_Individual.Date
FROM T_Individual INNER JOIN (T_Clients INNER JOIN (T_Jobs INNER JOIN T_IndivJobs ON T_Jobs.ID_Jobs = T_Individual.Jobs) ON T_Clients.ID_Client = T_Jobs.Client) ON T_Individual.ID_IndivRecords = T_IndivJobs.ID_DonorRecords
WHERE (((T_Individual.Date)=Date()));
strSQL = "UPDATE [" & strQry & "] SET [FormatName] = REPLACE(REPLACE(REPLACE([FormatName],' ','<>'),'><',''),'<>',' '), " & _
"[CDSal] = REPLACE(REPLACE(REPLACE([FormatName],' ','<>'),'><',''),'<>',' ');"
Another instance of a simple error and or mistype can drastically affect everything you are trying to achieve. This SQL was ran after the code was processed to remove any double spaces that might have been in the original data or created from concatenation. Notice that the CDSal field will be replaced with the FormatName field in the last line instead of being replaced with itself. Since most records do not use the FormatName field their CDSal field was getting replaced with NULL . . .
I have corrected this issue and everything runs very smoothly and correctly now.
Thanks for everyone who tried to help on this! Any additional information on Formatting or Optimization is always appreciated.

Multiple Combo boxes filtering Listbox (Revisited?)

I'm attempting to filter a listbox based on several combo boxes. Seems pretty easy, right? In fact, I found pretty much the exact answer to my problem, however I can't seem to get it to work properly. (see: Multiple Combo Boxes to filter a listbox)
Using the code (modified for my purposes obviously) from the solution above doesn't seem to want to filter out anything specifically. Instead, it isn't finding any records in the query that match the filtering at all.
I have five Combo Boxes which grab unique values from a query (qryCustomerWants) and populate each of the five combo boxes based on the appropriate column in the query. When I click one of the combo boxes, the list box updates and is supposed to filter down the results based on the search criteria selected in the combo boxes.
Private Sub RequerylstCustomers()
Dim SQL As String
SQL = "SELECT qryCustomerWants.ID, qryCustomerWants.Type, qryCustomerWants.Make, qryCustomerWants.Model, qryCustomerWants.YearWanted, qryCustomerWants.Condition " _
& "FROM qryCustomerWants " _
& "WHERE 1=1 "
If cboType.Value & "" <> "" Then
SQL = SQL & " AND qryCustomerWants.Type = '" & cboType.Value & "'"
End If
If cboMake.Value & "" <> "" Then
SQL = SQL & " AND qryCustomerWants.Make = '" & cboMake.Value & "'"
End If
If cboModel.Value & "" <> "" Then
SQL = SQL & " AND qryCustomerWants.Model = '" & cboModel.Value & "'"
End If
If cboYear.Value & "" <> "" Then
SQL = SQL & " AND qryCustomerWants.Year = '" & cboYear.Value & "'"
End If
If cboCondition.Value & "" <> "" Then
SQL = SQL & " AND qryCustomerWants.Condition = '" & cboCondition.Value & "'"
End If
SQL = SQL & " ORDER BY qryContactWants.Last"
Me.lstCustomers.RowSource = SQL
Me.lstCustomers.Requery
End Sub
I call the function using:
Private Sub cboType_AfterUpdate()
RequerylstCustomers
End Sub
Currently, each time I select an item from a combo box (any of them) it wipes the entire listbox clear. I know there are records that match the search parameters, so it should be filtering these down to a smaller list each combo box I select an entry from.
Where I am messing this up? thanks!
I see right now, that your Order By uses qryContactWants and not qryCustomerWants.
I guess that's the reason for your problem.

Access Append Query In VBA From Multiple Sources Into One Table, Access 2010

I hardly ever post for help and try to figure it out on my own, but now I’m stuck. I’m just trying to append data from multiple tables to one table. The source tables are data sets for each American State and the append query is the same for each State, except for a nested select script to pull from each State table. So I want to create a VBA script that references a smaller script for each state, rather than an entire append script for each state. I’m not sure if I should do a SELECT CASE, or FOR TO NEXT or FOR EACH NEXT or DO LOOP or something else.
Here’s what I have so far:
tblLicenses is a table that has the field LicenseState from which I could pull a list of the states.
Function StateScripts()
Dim rst As DAO.Recordset
Dim qryState As String
Dim StateCode As String
Set rst = CurrentDb.OpenRecordset("SELECT LicenseState FROM tblLicenses GROUP BY LicenseState;")
' and I've tried these, but they don't work
' qryState = DLookup("LicenseState", "tblLicenses")
' qryState = "SELECT LicenseState INTO Temp FROM tblLicenses GROUP BY LicenseState;"
' DoCmd.RunSQL qryState
Select Case qryState
Case "CT"
StateCode = "CT"
StateScripts = " SELECT [LICENSE NO] AS StateLicense, [EXPIRATION DATE] AS dateexpired FROM CT "
Case "AK"
StateCode = "AK"
StateScripts = " SELECT [LICENSE] AS StateLicense, [EXPIRATION] AS dateexpired FROM AK "
Case "KS"
StateCode = "KS"
StateScripts = " SELECT [LicenseNum] AS StateLicense, [ExpDate] AS dateexpired FROM KS "
End Select
CurrentDb.Execute " INSERT INTO TEST ( StLicense, OldExpDate, NewExpDate ) " _
& " SELECT State.StateLicense as StLicense, DateExpire AS OldExpDate, State.dateexpired AS NewExpDate " _
& " FROM ( " & StateScripts & " ) AS State " _
& " RIGHT JOIN tblLicenses ON (State.StateLicense = tblLicenses.LicenseNum) " _
& " GROUP BY State.StateLicense, DateExpire, State.dateexpired " _
& " HAVING (((LicenseNum) Like '*" & StateCode & "*') ; "
End Function
It sounds like you are dealing with input sources that use different column names for the same information, and you are working to merge it all into a single table. I will make the assumption that you are dealing with 50 text files that are updated every so often.
Here is one way you could approach this project...
Use VBA to build a collection of file names (using Dir() in a specific folder). Then loop through the collection of file names, doing the following:
Add the file as a linked table using VBA, preserving the column names.
Loop through the columns in the TableDef object and set variables to the actual names of the columns. (See example code below)
Build a simple SQL statement to insert from the linked table into a single tables that lists all current license expiration dates.
Here is some example code on how you might approach this:
Public Sub Example()
Dim dbs As Database
Dim tdf As TableDef
Dim fld As Field
Dim strLic As String
Dim strExp As String
Dim strSQL As String
Set dbs = CurrentDb
Set tdf = dbs.TableDefs("tblLinked")
' Look up field names
For Each fld In tdf.Fields
Select Case fld.Name
Case "LICENSE", "LICENSE NO", "License Num"
strLic = fld.Name
Case "EXPIRATION", "EXPIRATION DATE", "EXP"
strExp = fld.Name
End Select
Next fld
If strLic = "" Or strExp = "" Then
MsgBox "Could not find field"
Stop
Else
' Build SQL to import data
strSQL = "insert into tblCurrent ([State], [License],[Expiration]) " & _
"select [State], [" & strLic & "], [" & strExp & "] from tblLinked"
dbs.Execute strSQL, dbFailOnError
End If
End Sub
Now with your new table that has all the new data combined, you can build your more complex grouping query to produce your final output. I like this approach because I prefer to manage the more complex queries in the visual builder rather than in VBA code.
Thanks for your input. I came up with a variation of your idea:
I created table ("tblStateScripts"), from which the rs!(fields) contained the various column names
Dim rs As DAO.Recordset
Dim DB As Database
Set DB = CurrentDb
Set rs = DB.OpenRecordset("tblStateScripts")
If Not rs.EOF Then
Do
CurrentDb.Execute " INSERT INTO TEST ( StLicense, OldExpDate, NewExpDate ) " _
& " SELECT State.StateLicense as StLicense, DateExpire AS OldExpDate, State.dateexpired AS NewExpDate " _
& " FROM ( SELECT " & rs!FldLicenseState & " AS StateLicense, " & rs!FldExpDate & " AS DateExp " & " FROM " & rs!TblState " _
& " RIGHT JOIN tblLicenses ON (State.StateLicense = tblLicenses.VetLicense) " _
& " GROUP BY State.StateLicense, DateExpire, State.dateexpired " _
& " HAVING (((LicenseNum) Like '*" & rs!StateCode & "*') ; "
rs.MoveNext
Loop Until rs.EOF
End If
rs.Close
Set rs = Nothing

Microsoft Access passing variables to VB from Form

I built a form with 2 combo boxes, one for Event and one for People. The two queries for the boxes include the Event_id and People_id but are not shown. The idea is to select the event from a drop down then add a person to attend the event. My problem is passing these two ID to the SQL update script.
Below is the VB; I receive a 424 error and calls the SQL as the area.
What's wrong?
Private Sub Command15_Click()
Dim dbs As DAO.Database, sql As String, rCount As Integer
Set dbs = CurrentDb
sql = "INSERT INTO Whos_Going (Event_ID, Who_is_invited) " _
& "VALUES(" & Event_id.Text & ", " & People_id.Text & ")"
dbs.Execute sql, dbFailOnError
rCount = dbs.RecordsAffected
If rCount > 0 Then
MsgBox "Person Added to Event"
'update listbox
conInfo.Requery
End If
End Sub
With VBA you need to construct the valid sql statement. All string literals must be enclosed into ', all dates must be enclosed into #. In addition to that, if your string variable contains an apostrophe it needs to be replaced with ''. The correct syntax will be:
sql = "INSERT INTO Whos_Going (Event_ID, Who_is_invited) " _
& "VALUES('" & Replace(Event_id.Text, "'", "''") & "', '" _
& Replace(People_id.Text, "'", "''") & "')"

Using Cell reference in SQL query

I am using below VBA command to extract data using a SQL query by using a cell reference as filter.
Public Sub Run()
Dim SQL As String
Dim Connected As Boolean
Dim server_name As String
Dim database_name As String
Dim Allocation As String
Sheets("Perc").Select
Range("A6").Select
Selection.CurrentRegion.Select
Selection.ClearContents
' Our query
SQL = "SELECT Segmentation_ID,MPG,SUM(Segmentation_Percent) AS PERC " & _
"FROM dbo.HFM_ALLOCATION_RATIO " & _
"WHERE Segmentation_ID = " & Sheets("Perc").Range("A1").Value & " " & _
"GROUP BY Segmentation_ID,MPG ORDER BY MPG"
' Connect to the database
server_name = Sheets("General").Range("D1").Value
database_name = Sheets("General").Range("G1").Value
Connected = Connect(server_name, database_name)
If Connected Then
' If connected run query and disconnect
Call Query(SQL)
Call Disconnect
Else
' Couldn't connect
MsgBox "Could Not Connect!"
End If
End Sub
But when I run the macro, it's showing a runtime error
Invalid column name ALO0000274
Here ALO0000274 is the filter I set in A1.
Kindly help to fix this error.
Add quotes to your where clause (you're passing a text value, so you need to include quotes):
SQL = "SELECT Segmentation_ID,MPG,SUM(Segmentation_Percent) AS PERC " & _
"FROM dbo.HFM_ALLOCATION_RATIO " & _
"WHERE Segmentation_ID = '" & Sheets("Perc").Range("A1").Value & "' " & _
"GROUP BY Segmentation_ID,MPG ORDER BY MPG"
(Note the single quotes ' that enclose the value you want to filter)
I don't see where you are adding your single quotes around your string. Have you tried changing your WHERE clause portion of your SQL statement to "WHERE Segmentation_ID = '" & Sheets("Perc").Range("A1").Value & "' "