Sumproduct in Excel VBA using function arguments as source - sql

I'm trying to write a VBA function that takes the function arguments as the source and then runs SQL commands on it. The SQL command I'm currently trying to implement as a first step is the equivalent of Excel's sumproduct() function, which in SQL would be something along the lines of "SELECT SUM(A * B)".
I modified some code from another site that used SQL to convert an Excel table into a single column, and modified it to calculate a sumproduct, but I'm having trouble converting the subroutine into a function.
Current working code:
Sub doSQL()
Dim strCon As String
Dim oneSQL As String
' refer to 'microsoft activex data objects library'
Dim cn As Object
Dim rs As Object
Set cn = CreateObject("ADODB.Connection")
Set rs = CreateObject("ADODB.Recordset")
strCon = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source='" & ThisWorkbook.FullName & "';" & _
"Extended Properties='Excel 12.0;HDR=No;IMEX=1';" ' HDR=No means no headers (field names)
cn.Open strCon ' open connection
'-------------------------------------------------------------------------------
' F1, F2, F3 are the default fieldnames when no headers are included with data
oneSQL = "SELECT sum(F1 * F2) FROM [Sheet1$B:D] where F1 not like '' AND F2 not like ''"
rs.Open oneSQL, cn ' get recordset
Sheets("Sheet1").Range("A:A").ClearContents
Sheets("Sheet1").Range("A1").CopyFromRecordset rs ' copy recordset to worksheet
'-------------------------------------------------------------------------------
rs.Close
cn.Close
Set rs = Nothing
Set cn = Nothing
End Sub
The end result is that cell A1 returns the equivalent of sumproduct(B3:B5,C3:C5).
My attempt to convert it into a function:
Function SQL_sumproduct(A As Variant, B As Variant) As Double
Dim strCon As String
Dim oneSQL As String
' refer to 'microsoft activex data objects library'
Dim cn As Object
Dim rs As Object
Set cn = CreateObject("ADODB.Connection")
Set rs = CreateObject("ADODB.Recordset")
strCon = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source='" & ThisWorkbook.FullName & "';" & _
"Extended Properties='Excel 12.0;HDR=No;IMEX=1';" ' HDR=No means no headers (field names)
cn.Open strCon ' open connection
oneSQL = "SELECT sum(A * B)"
rs.Open oneSQL, cn ' get recordset
'-------------------------------------------------------------------------------
' Sheets("Sheet1").Range("A:A").ClearContents
' Sheets("Sheet1").Range("A3").CopyFromRecordset rs ' copy recordset to worksheet
'-------------------------------------------------------------------------------
SQL_sumproduct = rs
rs.Close
cn.Close
Set rs = Nothing
Set cn = Nothing
End Function
I end up getting an error because the code does not recognise the function arguments A and B as a source.
Could anyone give me some guidance as to how the "Data Source" parameter can be modified to recognise the function arguments?

The SQL statement in oneSQL = "SELECT sum(A * B)" is looking for fields named "A" and "B" (regardless of what is passed into the function in the parameters A and B). This is because A and B are enclosed in double-quotes.
To incorporate the parameters into the SQL statement, you would write this instead:
oneSQL = "SELECT sum(" & A & " * " & "B)"
Having A and B outside the double quotes causes them to be replaced with their values. & is the string concatenation operator which joins all of the pieces together.
Note: when dynamically constructing an SQL statement from any kind of external input, consider the possibility of SQL injection - see here and here

Related

VBA, Import CSV split by ";" to sheet

I am trying to import a CSV file split by semicolon ";" into an excel object so I can use it later on.
Ideally i would like to use ADO, DAO or ADODB so I can also run SQL queries on the object, and get sum of specific fields, or total number of fields and so on.
So far i've gotten the code below, but it does not split the data by ";", so it all comes back as 1 field instead of multiple fields that can be handled.
Sub Import()
Dim conn As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim f As ADODB.Field
Dim csvName, csvPath
csvPath = ActiveWorkbook.path
csvName = "fileName.csv"
conn.Open "DRIVER={Microsoft Text Driver (*.txt; *.csv)};DBQ=" & csvPath & ";"
rs.Open "SELECT * FROM " & csvName, conn, adOpenStatic, adLockReadOnly, adCmdText
Debug.Print rs.Fields
While Not rs.EOF
For Each f In rs.Fields
Debug.Print f.Name & "=" & f.Value
Next
Wend
End Sub
Can anyone give me an idea how I can also split the data by ";" and query it using SQL query? Or a different object that I could load a CSV into and query certain columns.
Here's example:
Public Sub QueryTextFile()
Dim rsData As ADODB.Recordset
Dim sConnect As String
Dim sSQL As String
' Create the connection string.
sConnect = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=C:\Files\;" & _
"Extended Properties=Text;"
' Create the SQL statement.
sSQL = "SELECT * FROM Sales.csv;"
Set rsData = New ADODB.Recordset
rsData.Open sSQL, sConnect, adOpenForwardOnly, _
adLockReadOnly, adCmdText
' Check to make sure we received data.
If Not rsData.EOF Then
' Dump the returned data onto Sheet1.
Sheet1.Range("A1").CopyFromRecordset rsData
Else
MsgBox "No records returned.", vbCritical
End If
' Clean up our Recordset object.
rsData.Close
Set rsData = Nothing
End Sub
The only answer I found that was usable was to create an ini file in the current folder, and enter the delimiter in the ini file.
iniPath = activeworkbook.path & "\"
iniName = "schema.ini"
iniPathName = iniPath & iniName
If Not fso.FileExists(iniPathName) Then
fso.CreateTextFile (iniPathName)
End if

VBA ADODB- Select query using the excel sheet of the same workbook as Database

I am novice in VBA so please don't mind if the question is of low level.I am trying to run a SQL query where the data has to be extracted from one of the sheets of the same workbook.
SQL = "Select ProductNumber from [sData$] where ProductSource = " & pSource & "
'pSource is a string that stores Product Source
'sdata is a sheet named as Data in the workbook
dataPath = ThisWorkbook.Fullname
'Not sure if this is the value I shall send as datapath in getData function
Set rst = getData(dataPath,SQL)
rst.Open
The getData function is defines as below
Public funtion getData(path as String, SQL as string) as ADODB.Recordset
Dim rs as ADODB.Recordset
Set cn = New ADODB.Connection
Set rs = New ADODB.Recordset
cn.Open ("Provider= Microsoft.Jet.OLEDB.4.0;" & _
"DataSource= " & path & ";"&_
"Extended Properties=""Excel 8.0;HDR=Yes;FMT=Delimited;IMEX=1;""")
rs.ActiveConnection =cn
rs.Source= SQL
Set getData =rs
End Function
Now after I get the numbers from Data sheet, I need to find the corresponding
ProductCompany from Relation sheet. 9 is for Amul, 5 is for Nestle and so on.
Relation:
I am not sure how to do that. The numbers corresponds to their respective Product company in order.
Take a look at the below example showing how to create ADODB connection to this workbook, get ADODB recordset from SQL query, retrieve key - value pairs from relation sheet, create and populate a dictionary, and output the values from the recordset and the corresponding values from the dictionary:
Option Explicit
Sub Test()
Dim oCn As Object
Dim oRs As Object
Dim aKeys
Dim aItems
Dim i As Long
Dim oDict As Object
Dim dProdNum
' create ADODB connection to this workbook
Set oCn = CreateObject("ADODB.Connection")
oCn.Open _
"Provider=Microsoft.Jet.OLEDB.4.0;" & _
"DataSource='" & ThisWorkbook.FullName & "';" & _
"Extended Properties=""Excel 8.0;HDR=Yes;FMT=Delimited;IMEX=1;"";"
' get ADODB recordset from SQL query
Set oRs = oCn.Execute("SELECT DISTINCT ProductNumber FROM [Data$] WHERE ProductSource = 'A1'")
' retrieve key - value pairs from relation sheet
With ThisWorkbook.Sheets("Relation")
aKeys = Split(.Range("B1"), ",")
aItems = Split(.Range("B2"), ",")
End With
' create and populate a dictionary
Set oDict = CreateObject("Scripting.Dictionary")
For i = 0 To UBound(aKeys)
oDict(Trim(aKeys(i)) + 0) = Trim(aItems(i))
Next
' output the values from the recordset and the corresponding values from the dictionary
oRs.MoveFirst
Do Until oRs.EOF
dProdNum = oRs.Fields(0).Value
Debug.Print dProdNum & " - " & oDict(dProdNum)
oRs.MoveNext
Loop
End Sub
The output for me is as follows:
4 - Britanica5 - Nestle9 - Amul
Note, connection string in the above code shown for .xls file. In case .xlsm you should use:
oCn.Open _
"Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source='" & ThisWorkbook.FullName & "';" & _
"Extended Properties=""Excel 12.0 Macro;HDR=Yes;FMT=Delimited;IMEX=1;"";"

Saving Results of Access Query To Worksheet Excel VBA

I cant seem to find an easy way of doing outside of just accessing the SQL from ACCESS SQL View and doing it manually. Is there some magic way to use this code below and do that?
Its worth pointing out that I am trying to do this from Excel's VBA.
Private Sub tryagain()
Dim con As ADODB.Connection
Dim rs As ADODB.Recordset
Set con = New ADODB.Connection
With con
.Provider = "Microsoft.ACE.OLEDB.12.0"
.Open "C:\Users\Ashleysaurus\Desktop" & "\" & "xyzmanu3.accdb"
End With
con.Execute "Invoice Query"
'How do output to Worksheet?
rs.Close
cmd.ActiveConnection.Close
End Sub
Simply use the ADO recordset object which you initialize, call the query, and then run the Range.CopyFromRecordset method (specifying the leftmost worksheet cell to place results).
Also, see the changed connection open routine with proper connection string. And because recordsets do not pull in column headers automatically but only data, an added loop was included iterating through recordset's field names.
Private Sub tryagain()
Dim con As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim strConnection As String
Dim i as Integer, fld As Object
strConnection = "Provider=Microsoft.ACE.OLEDB.12.0;" _
& "Data Source='C:\Users\Ashleysaurus\Desktop\xyzmanu3.accdb';"
con.Open strConnection
rs.Open "SELECT * FROM [Invoice Query]", con
' column headers
i = 0
Sheets(1).Range("A1").Activate
For Each fld In rs.Fields
ActiveCell.Offset(0, i) = fld.Name
i = i + 1
Next fld
' data rows
Sheets(1).Range("A2").CopyFromRecordset rs
rs.Close
cn.Close
End Sub
By the way, this same above setup can even query Excel workbooks as the Jet/ACE SQL Engine is a Windows technology (.dll files) available to all Office or Windows programs.
strConnection = "Provider=Microsoft.ACE.OLEDB.12.0;" _
& "Data Source='C:\Path\To\Workbook.xlsm';" _
& "Extended Properties=""Excel 8.0;HDR=YES;"";"
strSQL = "SELECT * FROM [Sheet1$]"

Update Excel data via SQL UPDATE

I'd like to update my Excel sheet's data via an SQL query. I know you can 'connect' to a sheet via ADODB.Connection and retrieve (SELECT) data from it in a ADODB.Recordset. However using the same process for an UPDATE query produces an 'Operation must use an updateable query' error. Is there any other way to achieve this?
An example code that produces the error:
Sub SQLUpdateExample()
Dim con As ADODB.Connection
Dim rs As ADODB.Recordset
Set con = New ADODB.Connection
con.Open "Driver={Microsoft Excel Driver (*.xls)};" & _
"DriverId=790;" & _
"Dbq=" & ThisWorkbook.FullName & ";" & _
"DefaultDir=" & ThisWorkbook.FullName
Set rs = New ADODB.Recordset
Set rs = con.Execute("UPDATE [Sheet1$] SET a = 10 WHERE b > 2")
Set rs = Nothing
Set con = Nothing
End Sub
The code expects to be in a saved .xls worksheet where Sheet1 includes a table with column headers (at least) a and b.
Didn't think that would be all to make it work...
Answer
Connections to Excel files are set to ReadOnly as default.
You have to add ReadOnly=False to your Connection String.
More information can be found at Microsoft Support

ADODB object ignoring text in excel

I have a data table in excel with both numbers and text in the store number column. I need to use sql through an ADODB object in VBA to get a list of the store numbers in the column. I'm using the following to set up the ADODB.
The problem: If the query hits a number first then the field is treated as numbers and texts are ignored. and if a text is hit first the numbers are ignored.
I'm using the following to set up the ADODB. These are in the worksheet module hints me..
Public Function Query(qry As String) As ADODB.Recordset
Dim cn As ADODB.Connection '* Connection String
Dim rs As ADODB.Recordset '* Record Set
Dim sQuery As String '* Query String
Dim FileName As String
On Error GoTo QryErr
Set cn = New ADODB.Connection
FileName = ThisWorkbook.Path & "\" & ThisWorkbook.Name
cn.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & FileName & _
";Extended Properties=Excel 8.0;Persist Security Info=False"
cn.ConnectionTimeout = 40
cn.Open
Set rs = New ADODB.Recordset
Select Case LCase(qry)
Case "dry": sQuery = SQLDry
Case "frz": sQuery = SQLFrz
Case "fsh": sQuery = SQLFsh
Case "temp": sQuery = SQLTemp
Case Else: GoTo QryErr
End Select
rs.ActiveConnection = cn
rs.Source = sQuery
rs.Open
Set Query = rs
If Not rs Is Nothing Then Set rs = Nothing
If Not cn Is Nothing Then Set cn = Nothing
QryErr:
If Err <> 0 Then
Debug.Assert Err = 0
MsgBox Err.Description
End If
End Function
And I have the following for the SQL portion.
Private Function SQLTemp() As String
Dim Name As String
Name = Me.Name
SQLTemp = "SELECT str([" & Name & "$].Store) as Store " & _
"FROM [" & Name & "$] " & _
"GROUP BY [" & Name & "$].Store"
End Function
As you can see I've tryed converting the field to str() but it doesn't help.
How can I get the query to treat the column as text so that all values are returned. I'd like to avoid putting ' in front of all my numbers.
Asked 5 years ago! I had the same problem. The solution for me was to change the data type of the offending columns (those with "special characters" and "TEXT" datatype) to "VARCHAR", or you can remove in the query the special characters, or change the CharacterSet by means of Schema.ini, as #Tim Williams suggests (msdn.microsoft.com/en-us/library/ms709353(v=vs.85).aspx).