Reputation: 15
Edit: The solution below helped in a way that combined all my select statements into one string, but I ended up using unions because I had too many tags and SQL would not take the amount of OR clauses. The exact solution below might help someone who has less elements.
I have a dynamic string list that i want to section off into batches of 4000 so i can combine those 4000 into one string. Currently i have a program that assumes there is at most 8000 items, which is giving me errors since the list extended beyond that. I was thinking of using a dynamic variable in VBA (which i learned in C++) but it does not apply in VBA sadly... The tagcapture() is used to grab the item name from a worksheet column. MaxTagPull is 3999 to account for the 0th item being counted.
For CurrentIndex = 2 To LastTagIndex
If RegTagCount = 0 Then ' If first regular tag in string
String2 = "'" & tagcapture(1, CurrentIndex) & "'"
String3 = String3 & String2
End If
If RegTagCount > 0 And RegTagCount < MaxTagPull Then ' If second tag and up until max number of tags allowed in one query string
String2 = ", '" & tagcapture(1, CurrentIndex) & "'"
String3 = String3 & String2
End If
If RegTagCounter = MaxTagPull Then ' Put the rest of the regular tags in a separate string
String4 = "'" & tagcapture(1, CurrentIndex) & "'"
String5 = String5 & String4
End If
If RegTagCounter > MaxTagPull Then ' Put the rest of the regular tags in a separate string
String4 = ", '" & tagcapture(1, CurrentIndex) & "'"
String5 = String5 & String4
End If
RegTagCount = RegTagCount + 1 ' Counts the amount of regular tags encountered
Next
How do i modify this so it can accommodate the dynamic list? Maybe using a function or another sub? Note that the total number of items is known, so maybe something could be done with that?
Thank you
Upvotes: 1
Views: 90
Reputation: 166595
This is not the exact question you asked, but I think it meets your final goal:
Sub Tester()
Debug.Print SQLInClause(Range("B3:B29"), "myFieldName", False)
End Sub
'Given a single-column range "rng", convert the content to a SQL "in" clause,
' with a maximum number of items per "in" block, with blocks joined by "or"
' Use the SQL field name supplied in "fName"
' "isChar" determines whether to quote the values
Function SQLInClause(rng As Range, fName As String, isChar As Boolean) As String
Const MAX_PER_BLOCK As Long = 10 '<<10 for testing: set to 4000 for production use...
Dim sql As String, arr(), i As Long, n As Long
Dim d, sep, qt, tot As Long
d = rng.Value
tot = UBound(d, 1)
i = 0 'counter for each block
qt = IIf(isChar, "'", "") '<< set quote character (or none)
sep = ""
ReDim arr(1 To MAX_PER_BLOCK)
For n = 1 To tot
'any value to add?
If Len(d(n, 1)) > 0 Then
i = i + 1
arr(i) = d(n, 1)
'are we at the end of a block, or at the end of the list?
If i = MAX_PER_BLOCK Or n = tot Then
'if at the end of the list, resize array to trim off empty elements
If n = tot Then ReDim Preserve arr(1 To i)
sql = sql & sep & fName & " in(" & qt & Join(arr, qt & "," & qt) & qt & ")"
sep = vbCrLf & " or " '<< "or" is set after first block
i = 0
End If
End If
Next n
SQLInClause = sql
End Function
Sample output (formatted a little):
myFieldName in('Val0001','Val0002','Val0003','Val0004','Val0005','Val0006',
'Val0007','Val0008','Val0009', 'Val0010')
or myFieldName in('Val0011','Val0012','Val0013','Val0014','Val0015','Val0016',
'Val0017','Val0018','Val0019', 'Val0020')
or myFieldName in('Val0021','Val0022','Val0023','Val0024','Val0025','Val0027')
Upvotes: 1