Long Query Execution through VBA concatenation - sql

I'm setting up an automated excel vba workbook that extracts data through a Query, while getting the parameters from Excel cells.
So far it has worked on simple Queries and SPs but I'm trying to pass a very long query and it seems Excel Functions is truncating the string to 1043 chars.
*The query has 1426 chars.
I've taken 3 different approaches to this problem.
Executing a SQL stored procedure (from vba) rather than a query, the problem is one of the input parameters is a list of ids, which is variable in length, and very long to pass as an SP parameter, so it hasn't worked even when assigning Varchar(Max) as type.
Executing the query into a new table, and extracting the full table into excel. While this does work it makes it necessary to update the id list manually, hence it's not functional enough.
Passing the SQL string directly to the Excel function as a concatenation of strings, but it throws the error which I believe relates to the string truncate.
My code is as follows:
Private Sub CommandButton1_Click()
Dim organization As Integer 'Business Unit
Dim material As String 'List of IDs
organization = Sheets("Hoja1").Range("B3").Value 'Extract B3
material = Sheets("Hoja1").Range("B4").Value 'Extract B4
'Parsing the query into statements to be concatenated
Connection
With ActiveWorkbook.Connections("NZSQL Test").ODBCConnection
sql1 = 'statement1
sql2 = 'statement2
sql3 = 'statement3
sql4 = 'statement4
sql5 = 'statement5
sql6 = 'statement6
Query = sql1 & sql2 & sql3 & sql4 & sql5 & sql6
Sheets("Hoja1").Range("B2") = Query 'This is to beused as revision directly
in ODBC platform
.CommandText = Query
ActiveWorkbook.Connections("NZSQL Test").Refresh
End With
End Sub
The resultset I'm getting when pasting the B2 query in SQL:
/*
ERROR [42000] ERROR: '...' 'Returns a part of the SQL string */
The string here is truncated to 1043 chars.
However, I have tried the same approach from Python using pyodbc and it works perfectly.

Have you tried explicitly declaring your Query variable as a string with your other declarations in the sub?
Dim Query As String
According to here, a string can:
A variable-length string can contain up to approximately 2 billion (2^31) characters.
A fixed-length string can contain 1 to approximately 64 K (2^16) characters.
Hopefully that helps.

Related

Database sum() values together if string matches in VBA

Sorry for the basic question, I've spent the best part of a hour figuring this out and I'm at my limit. I'm trying to add all values together if a string matches a certain size, for context I'm adding wall tiles together for a client, here's an example table;
Code
TileSize
TileQty
BG3215
8mm
1
BG3545
10mm
3
BG3246
8mm
4
BG3745
8mm
1
BG3255
12mm
2
My VBA:
Dim db As DAO.Database
Set db = CurrentDb
Dim eight As DAO.Recordset
Dim strSQLeight As String
strSQLeight= "SELECT SUM(TileQty) FROM PickQuery WHERE TileSize = '8mm'"
Set eight= db.OpenRecordset(strSQLeight)
'Error line ^
sometextbox = eight.Fields(0).Value
Run-Time error:
Too few parameters expected 3
Intended result is to return 6 (6 x "8mm" strings)
I assume there should be another argument in there but after looking in stack overflow and on google for help, I can't see another way to write this.
Your code works perfectly for me (running in an MSAccess DB, vba module):
Sub foo()
Dim db As DAO.Database
Set db = CurrentDb
Dim eight As DAO.Recordset
Dim strSQLeight As String
strSQLeight = "SELECT SUM(TileQty) FROM PickQuery WHERE TileSize = '8mm'"
Set eight = db.OpenRecordset(strSQLeight)
sometextbox = eight.Fields(0).Value
End Sub
Is there something we are missing? How are you running this code? Is it in an access vba module? Somewhere else?
Too few parameters expected 3
This means, that your query PickQuery is missing three parameter values.
Most likely, these are values fetched from the form where you call this code.

How to populate column based on other columns

I need to understand why this does not work in MS Access:
UPDATE main_records
SET main_records.rece = Str(main_records.Nr) & "," & Str(main_records.Pag);
The intent is to populate the rece column (63 chars string) in all records of main_records with the contents of Nr and Pag (converted to string and concatenated).
It looks so easy but ...
"the contents of Nr and Pag (converted to string and concatenated)"
If that is what you intend to do, you are to use a string conversion function
examples is
CStr( expression ).

Storing field names from a query table into a dynamic array MS Access

I have a Query qryRuleSets which outputs a table with 19 fields (that I do not want to save into an access table before that is suggested). I would like to get the field names and store them into an array so I can use that array in a for loop later on.
To find the number of fields that in the query result (to use in for loop later on) I have implemented the following, where the number of fields is stored in the variable numberfields -
numberfields = CurrentDb.QueryDefs("qryrulesets").Fields.Count
To actually get the name of these fields and store them in an array I am running into 2 problems:
1. Getting the field names from the query result
2. Setting up a dynamic array so that if the query ends up returning a table with more or less than 19 fields, it will still work
For my first problem:
I have tried to follow the steps in the following link: Get Column name from a query table but I can't figure it out.
To get the field names from the qry result I have tried the following but I'm not overly knowledgeable in vba/access so finding it hard to understand, even after a whole lot of googling:
Dim qry As QueryDef
Dim fieldNames As QueryDef
Dim firstcol As String
Set fieldNames = CurrentDb.CreateQueryDef(qry.qryrulesets)
firstcol = fieldNames.field(0).Name
For my second problem:
To store values in an array I have tried the following (as a test) and it works but I have to define the size of the array. Is there a way where it can be dynamic, i.e based on the value of the number of fields (found above stored in numberfields) :
Dim vardata(30) As Variant
For i = 1 To numberfields
vardata(i) = "hello"
Next i
I tried making the '30' above to a variable value but it didn't like that.
Any and all help will be appreciated. Thanks!
You can do like this:
Public Function GetFieldNames(ByVal QueryName As String) As String()
Dim Query As DAO.QueryDef
Dim FieldNames() As String
Dim Index As Integer
Set Query = CurrentDb.QueryDefs(QueryName)
ReDim FieldNames(0 To Query.Fields.Count - 1)
For Index = LBound(FieldNames) To UBound(FieldNames)
FieldNames(Index) = Query.Fields(Index).Name
Next
GetFieldNames = FieldNames()
End Function

Run list of SQL queries using conditions from range of cell values

I'm trying to run a list of SQL queries where a condition exists for "code" and the values sit in a range of cells on another sheet (from cells A2 to A385).
I have the code below, however, I get an invalid object name for SQLQueries!$A2:A385
So, I understand the syntax is not correct but I'm struggling to find the correct one regardless of reading numerous articles.
Sub RunSQLQueries()
'Select SQLQueries sheet
Sheets("SQLQueries").Activate
'Initializes variables
Dim cnn As New ADODB.Connection
Dim rst As New ADODB.Recordset
Dim ConnectionString As String
Dim StrQuery As String
'Setup the connection string for accessing MS SQL database
ConnectionString = "Provider=SQLOLEDB; Data Source=HOSTNAME; Initial Catalog=DBNAME; UID=domain\user; Integrated Security=SSPI"
'Opens connection to the database
cnn.Open ConnectionString
'Timeout
cnn.CommandTimeout = 900
'Build SQK queries
StrQuery = "SELECT * FROM table WHERE code IN (SELECT * FROM [SQLQueries!$A2:A385])"
'Performs the queries
rst.Open StrQuery, cnn
'Select Results sheet
Sheets("Results").Activate
'Dumps all the results from the StrQuery into cell A2 of the active sheet
Range("A2").CopyFromRecordset rst
End Sub
The result I'm expecting is for a SQL query to be run using each condition from the range of values with the results being populated on the "Results" sheet from cells A2 down
The query string is literally sent to the database server, and since your sql attempts to refer to an excel list that the server cannot access it returns an error. The server is looking for a table named [SQLQueries!$A2:A385]
To stick with your current plan, you will need to pass the the IN () clause literally or by vba variable that is formatted as such:
IN ( 'item1', 'item2' ...)
Note:You can remove single quotes if the items are numeric
I advise rethinking the plan by either
1) if it is possible to adjust things in the database side: Can you create a new reference table to join to the actual table or create a view that only returns desired rows? Then you would need a job that tweaks the filtering view/ table before running the query. The idea being you would not need to adjust the query each time bc a constant sql string would return the rows you need
Or
2) if the source table has say 100k rows or less, and data is not too wide, just select all the rows into excel in a new sheet, then filter that sheet (add a new column in excel that returns true using vlookup against your reference sheet) or use vlookup on your reference sheet to pull the desired columns
Here's a suggestion:
StrQuery = "SELECT * FROM table WHERE code IN (" & _
InList(Sheets("SQLQueries").Range("A2:A385"),True) & ")"
Function to create a SQL "in" list given a range:
Function InList(rng As Range, quoted As Boolean)
Dim qt, a, r As Long, c As Long, rv As String, v, sep As String
a = rng.Value
qt = IIf(quoted, "'", "")
For r = 1 To UBound(a, 1)
For c = 1 To UBound(a, 2)
v = Trim(a(r, c))
If Len(v) > 0 Then
rv = rv & sep & qt & v & qt
sep = ","
End If
Next c
Next r
InList = rv
End Function
Notes:
Pass False as the second argument if you have numeric values
I wouldn't use this for very large lists
Be very certain you're not at risk from possible SQL injection issues: parameterized queries are always preferable but do not work with "in" lists

Excel VBA insert array variable into single field in access

In my project I'm trying to save indefinite entry in excel sheet into the database (access). I'm trying to use vba to do so. However, because I don't know how long the entries will be, and I can't create so many fields in the database to fit the unknown amount of entries. And I can't INSERT an array variable into one field in the table. How can I do this?
Say I have below.
Dim SN(100) As String
SN(0)=123 'value of each entry
SN(1)=2412
'so on and so many entries
Then in the SQL statement,
"INSERT INTO [TableName] ([FieldName]) Values (SN)"
It will say RunTime Error "13" Type mismatch.
Is there any way the database field can accept a list or array as data type? So when I call the data out I can use the index to call the variable inside the list?
MS Access does not have such a data type, more primitive types like string, boolean, long. But consider joining all items in array using Join into a single string and save that string to database table. Then use Split to pull back into array as needed:
Sub ArrayIntoSQL()
Dim SN(100) As String, TN() As String
Dim SNstr As String,
SN(0) = 123 'value of each entry'
SN(1) = 2412
SNstr = Join(SN, "|") ' PIPE DELIMITER '
'123|2412|||||||||||||||||||||||||||||||||||||||||||||_
||||||||||||||||||||||||||||||||||||||||||||||||||||||'
strSQL = "INSERT INTO [TableName] ([FieldName]) Values ('" & SNstr & "')"
...
End Sub
Sub ArrayOutofSQL()
Dim SN() As String
Dim SNstr As String
strSQL = "SELECT [FieldName] FROM [TableName]"
...
Do While Not rst.EOF
SN() = Split(rst!FieldName, "|")
rst.MoveNext
Loop
...
End Sub
Because the array items may exceed Access' 255 character limit for short text data type, use the long text type (previously called memo) which does not have a declared limit.
However for best practices, wide tables (many columns) and stored objects like arrays in fields are more expensive than long tables (many rows) and linked objects. So simply save data iteratively:
For Each item in SN
strSQL = "INSERT INTO [TableName] ([FieldName]) Values (" & item & ")"
...
Next item
Of course, save values with an identifier like ID, Person, Date!