Listbox.Listcount not accurate - ms-access

Searched this and other sites but could not find an answer as to why I am not seeing a correct listcount for a listbox when adding items from a recordset.
Most other threads focus either on the count being off by one due to column headers on the listbox or a 65,534 physical limit.
I am seeing something a bit different
Running Access 2010 with linked SQL Server 2008 tables.
Code:
lstSearchResults.RowSource = ""
lstSearchResults.AddItem ("Material Number;Description")
Set rsParts = CurrentDb.OpenRecordset(strSQL, dbOpenSnapshot)
If rsParts.RecordCount > 0 Then
rsParts.MoveLast
rsParts.MoveFirst
lngCount = 0
Do While Not rsParts.EOF
lngCount = lngCount + 1
lstSearchResults.AddItem (rsParts.Fields("MaterialNumber") & ";" & rsParts.Fields("Description"))
rsParts.MoveNext
Loop
End If
Placing a breakpoint just after the loop I examined the following values:
lstSearchResults.ListCount - 1012
rsParts.RecordCount - 5255
lngCount - 5255

Since you're using the AddItem method, that means your list box's Row Source Type property must be "Value List". And that means the Record Source property is a string which contains that list of values. And there is a limit to the number of characters which can be included in a value list. Although I don't recall the number for that limit, I suspect that is the reason your value list does not contain all the values you expect.
But there is a simpler way to load the list box values. You have a query, strSQL, for which you open a recordset, then walk the recordset to add 2 field values from each row to the value list.
Use the SQL statement directly ... without the recordset as an intermediary.
Change the list box's Row Source Type to "Table/Query".
Use the SQL statement as the list box's Row Source property.
That approach is less fuss and avoids the Value List character limit. Then you only need to decide whether 1K or more rows is really appropriate for a list box. :-)

Related

Determine the type of a listbox bound column VBA

I am building a SQL string based on the selections in a multiselect listbox and to accomplish this, I am trying to determine whether or not the type of the bound column of my listbox is a string.
I wrote the following code:
If lstBox.ItemsSelected.Count > 0 Then
lstVar = lstBox.ItemData(i)
Debug.Print VarType(lstVar)
For Each varItem In lstBox.ItemsSelected
strCrit = strCrit & "," & "'" & lstBox.ItemData(varItem) & "'"
Next varItem
' remove first comma
strCrit = Mid(strCrit, 2)
On the debug.print VarType statement, it always results in the string type, even if the bound column of the list box is an autonumber field. I want to know if the type is numerical rather than string so that later, in my SQL statment
SELECT … FROM … WHERE … IN (x,y,z)
I can write the correct WHERE … IN criteria that won't later result in a type mismatch. Is there a way to accurately determine variable type for a list box bound column? Perhaps I should approach this another way, by looking at the type of the row source of the list box. Because I am using this function to iterate through multiple list boxes that build the where criteria, I need it to be dynamic and flexible for the reality that some list box bound columns will be numeric while others will be strings, so I can't just hard code it into the loop.
For a bound list box, you can get the type of the bound column by using the underlying recordset.
See the following code:
Dim rs As Object 'most likely DAO.Recordset but could be ADODB.Recordset
With lstBox
Set rs = .Recordset
Debug.Print rs.Fields(.BoundColumn - 1).Type
End With
This prints a DAO.DataTypeEnum if you're working with a DAO recordset, or an ADODB.DataTypeEnum if you're working with an ADODB one. These are easy to convert to VBA data types.
Mathieu Guindon's point about using parameters remains, though. I strongly recommend it.

Filter a ListBox that's filled with MySQL

I see a lot of people that filter their Userform's ListBox using a TextBox or a ComboBox.
I'd like it to do mine, but unlike them, my Listbox is filled using a MySQL Recordset, while they are using an Excel Spreadsheet
There is my actual code, where SelectProduct is my Userform and ListRef is my Listbox.
Requete = "SELECT Reference,Nom,Marque,PrixVente FROM Produits_Beta"
rs.Open Requete, oConnect
SelectProduct.ListeRef.Clear
SelectProduct.ListeRef.Column = rs.GetRows
The Listbox is soon going to show 700+ results and I need a way for my user to filter them to find what they need.
If I used a Spreadsheet to get the Listbox value, my Filter code owuld look like this.
(Code originally from Ralph)
Dim i As Long
Dim arrList As Variant
Me.ListeRef.Clear
If TheoricalSheet.Range("A" & TheoricalSheet.Rows.Count).End(xlUp).Row > 1 And Trim(Me.TXBoxFilter.Value) <> vbNullString Then
arrList = TheoricalSheet.Range("A1:A" & TheoricalSheet.Range("A" & TheoricalSheet.Rows.Count).End(xlUp).Row).Value2
For i = LBound(arrList) To UBound(arrList)
If InStr(1, arrList(i, 1), Trim(Me.TXBoxFilter.Value), vbTextCompare) Then
Me.ListeRef.AddItem arrList(i, 1)
End If
Next i
End If
If Me.ListeRef.ListCount = 1 Then Me.ListeRef.Selected(0) = True
I could, but I would need a way to paste all a MySQL table to an hidden Spreadsheet, and, again, I have no idea how to do so.
The canonical way to filter SQL data for display is to use a WHERE clause in your query. That will work when you have seven, seven hundred, or seven million rows.
You might try something like this:
SELECT Reference,Nom,Marque,PrixVente
FROM Produits_Beta
WHERE Produit LIKE CONCAT('filter value', '%')
ORDER BY Produit
LIMIT 100
If you give an empty string for filter value you'll get the first hundred rows; your user will quickly see that a filter is necessary.
With no filter value, you get WHERE Produit LIKE '%' to not filter. With Pom as the filter value you'll get WHERE Produit LIKE 'Pom%' That matches Pomme, Pomme de terre, and Pomade, for example.
Edit You can use %pom% in LIKE. Here's the thing, however: if your search term has % coming first, the DBMS cannot use index lookups to find your data, so the searches will be slower. With a thousand rows to search, this doesn't matter. With millions of rows, it matters a lot.
Many developers of this kind of software use queries a lot to filter their data. DBMSs are built for it. The whole point of a DBMS is to allow software to handle vast sets of data efficiently.
Pro tip: Always use ORDER BY in your queries. If you don't the database server is free to present results in any order it finds most efficient at the moment. That's called unstable sorting and it drives users crazy.
The Worksheet.Visible attribute has three options, as follows:
xlSheetVisible 'The usual visible worksheet.
xlSheetHidden 'Worksheet that is hidden but may be turned visible by the user.
xlSheetVeryHidden 'Worksheet that is hidden but may only be turned visible via VBA.
If you were to create a hidden sheet to receive this data, you could try it as such:
Let's say you have first created a Worksheet in your workbook, (vba)named ws. In order to fetch the data from this recordset, you'll have to loop through its records and copy the value of each to a row:
'Header
With ws
.Cells(1,1) = "Reference"
.Cells(1,2) = "Nom"
.Cells(1,3) = "Marque"
.Cells(1,4) = "PrixVente"
End With
'Rows
Dim i as Long: i = 2
with Requete
If not (.EOF and .BOF) then
.movefirst
Do until .EOF
ws.Cells(i,1) = .Fields("Reference")
ws.Cells(i,2) = .Fields("Nom")
ws.Cells(i,3) = .Fields("Marque")
ws.cells(i,4) = .Fields("PrixVente")
.MoveNext
i=i+1
Loop
End If
End With
Then, if you don't want the user to have access to your sheet, just do:
ws.visible = xlSheetVeryHidden
If you plan to refresh the query and fetch the data again, you'll have to clear your worksheet beforehand.
Also, it might be good to order your data in your SQL query, so that the listbox gets populated alphabetically, should it suit your needs.

Access VBA Search Loop

I am currently making an search loop in VBA for my database. In this database I Have 2 Tables, one with the customers records called Main and another one with search words called Sparr. The idea is to use a filter to filter out the customers that match any of the search words in the Sparr table. Any customer that does not match any of the search words gets added to another table called filteredCustomerT.
For example:
table "Main"
Field "Mail"
mike#coolmail.com
john#hotmail.com
dave#mail.com
jonny#mailx.com
table "Sparr"
Field "sparrord"
hotmail
jonny
table "Testtable"
Field "testMail"
mike#coolmail.com
dave#mail.com
So if I run this VBA code I want john#hotmail.com and jonny#mailx.com to be filtered out. The Main table contains 200k records and the Sparr table contains 2k search words. I have wrote some VBA code that should loop through the Main table. For every record in the Main table a have another nested loop that loops through the Sparr table so see if there is any match. If there is not a match the VBA code copies the entry to anther table called Testtable. I Use the inStr function to do the matching.
Below I have posted the VBA code that does not seem to work. Can anyone help me ant maybe point out a fault in the code. I am very new to VBA programming.
Option Compare Database
Option Explicit
Sub filter()
Dim mainMail As Recordset
Dim sparrSokord As Recordset
Dim testtableTestmail As Recordset
Dim mainTemp As String
Dim sparrTemp As String
Dim match As Integer
Set mainMail = CurrentDb.OpenRecordset("Main")
Set sparrSokord = CurrentDb.OpenRecordset("Sparr")
Set testtableTestmail = CurrentDb.OpenRecordset("Testtable")
Do Until mainMail.EOF
mainTemp = mainMail![Mail]
match = 0
sparrSokord.MoveFirst
Do Until sparrSokord.EOF
sparrTemp = sparrSokord![sparrord]
If (InStr(mainTemp, sparrTemp) <> 0) Then
match = 1
Exit Do
End If
sparrSokord.MoveNext
Loop
If (match = 0) Then
testtableTestmail.AddNew
testtableTestmail![testMail] = mainTemp
testtableTestmail.Update
End If
mainMail.MoveNext
Loop
End Sub
InStr can operate in unexpected ways is you have nulls/empty strings/etc, involved.
I've noticed that you have nothing resetting the position of SearchwordWord back to the beginning of the record set once you reach the end.
Naturally you would do something like SearchwordWord.MoveFirst before the Do Until SearchwordWord.EOF. It doesn't hurt to do one before the Do Until customerMail.EOF either
I'll also note the Do Until always executes the contents of the loop, and then checks the condition at the end (which could be giving you unexpected results, especially with SearchwordWord being at EOF after the first successful pass of the loop.
You probably want to use a while/wend instead for both do untils (I practically never use them as it is). This is probably the biggest cause of your grief.
The problem is now solved. I just had to close some programs and try again with the updated code above. Worked just fine!

Using additional tables as lookup values in Access Query in IIF Statements?

I'm currently working with an Access(2010) Query that is taking multiple order items and GROUPING BY the order number. For example, a customer may have ordered 5 items under order number 123 but my query groups all of these individual items into one line with each item in it's own column. The objective is each individual order number is contained on one line because the query will be exported in a .csv format to 3rd party software.
Since I'm combining multiple records/items I'm running into issues when the description field is getting truncated based on 255 characters. Since working in a query I don't see the option of changing the text field to a memo field, so that won't resolve the issue and the GROUP BY clause is capping all the text fields at 255 chars anyway.
So, my question is instead of using the description fields from the current query is there a way that I can use an additional table to lookup an items description based on the part number ordered? Proposed new table would be something very simple like:
PART | DESC
123 Widget Z_Langs_AUS_INT<br>
567 Widget K_Langs_DOM_CAN<br>
890 Widget B_Langs_SM
So the ideal statement in the query would be something like IIF TABLE1.PART#ORDERED(from current query) = NEWTABLE.PART(from new table), then obtain NEWTABLE.DESC(from new table) otherwise ""
This would return the description of a specific part number if the part number fields matched otherwise leave the field blank because it likely doesn't contain a part number.
I'd appreciate any suggestions at this point. I may be going about this all wrong when trying to resolve this issue so fresh input is welcome.
Don't write that as a query. It's too complex to bother with, and is extremely simple to do with VBA and direct text access. Add a reference to the Microsoft Scripting Runtime (scrun.dll), use internal query objects for your SQL data, and use code similar to what's below.
Sub makeText()
Dim rs1 As Recordset, rs2 As Recordset
Dim oFSO As Scripting.FileSystemObject, oFile As Scripting.TextStream
Dim txtLine As String
Set oFSO = New Scripting.FileSystemObject
oFile = oFSO.CreateTextFile("FileName.csv")
Set rs1 = CurrentDb.OpenRecordset("baseQuery")
Do Until rs1.EOF
txtLine = rs1!OrderNumber.Value
Set rs2 = CurrentDb.OpenRecordset("innerQuery")
Do Until rs2.EOF
txtLine = txtLine & "," & rs2!itemDesc.Value
rs2.MoveNext
Loop
oFile.WriteLine txtLine
rs1.MoveNext
Loop
oFile.Close
End Sub
Below is a picture from an example database we used to use. It has our parts with all the info you could need, and then the part that matches up with our vendor. The query used will display each and every row where our part matches the vendor part.
You can also just put a [AskMeForMyPart] or whatever in square brackets like that to make the query pull based on that specific part.

getting minimum value in a recordset field without Looping

I am using Access 2007 and writing a macro in VBA.
"Analyze" subroutine is passed a recordset rev_rec. This recordset has two fields "PeriodNo" (Integer) and "Revenue"(double).
Minrev is a variable where I have to store the minimum value of "Revenue" field. The code that I am using is below.
Public Sub Analyze(rev_rec As DAO.Recordset)
Dim Minrev As Double
rev_rec.MoveFirst
Minrev = rev_rec("Revenue")
While Not rev_rec.EOF
If rev_rec("Revenue") < Minrev Then
Minrev = rev_rec("Revenue")
End If
rev_rec.MoveNext
Wend
rev_rec.MoveFirst
.
.
.
End Sub
I tried using DMin() with recordset but am not able to find a way to do that. Is there a way to do this without using any kind of loop?
I think your best bet is to build a new recordset using a SQL statement that only retrieves one record, the one with the minimum for the desired period. Another option is, you could open this particular recordset with the Order By on the Revenue column Ascending. This way you would know that the smallest value will be in the first record.
Andy Brown's suggestion of using DMin should also work. It's actually an idea very similar to my first suggestion.
The problem is that you're passing a recordset. If you just had the table or query name (or SQL statement), you could just use DMIN. Eg:
MinRev = DMIN("Revenue","TableOrQueryNameInQuotes","")
The third argument can be used to set some criteria. For example:
MinRev = DMIN("Revenue","TableOrQueryNameInQuotes","PeriodNo > 5")
Be warned, however, that the functions starting with D (DMIN, DLOOKUP, DSUM) run very slowly, although if you've got less than about 10,000 records you won't notice this.