Access VBA - print last record from SQL Query - ms-access

I have the following SQL Query I'm executing and I'm trying to find why it returns the error 'overflow' when running the query.
Now I want to print the last record that it computes before going into overflow, is this possible using MS Access VBA?
Private Sub Command0_Click()
Dim sql As String
Dim rs As DAO.Recordset
Dim db As DAO.Database
Set db = CurrentDb()
sql = "SELECT DatumNaarWeeknummer([tbl_ArtikelVerwijderdUitZaaglijst]![RegistratieDatum]) AS WeeknummerGezaagdeOmzet, " _
& "Sum([TotaalPrijs]/([tbl_ArtikelsPerOrder]![Aantal]*[Totaal])*[tbl_ArtikelVerwijderdUitZaaglijst]![Aantal]) AS GezaagdeOmzet " _
& "FROM (((tbl_ArtikelsPerOrder LEFT JOIN qry_Actieve_Orders ON tbl_ArtikelsPerOrder.OrderID = qry_Actieve_Orders.OrderID) LEFT JOIN qry_ArtikelPerOrderID_EenheidsPrijsBijFranco ON tbl_ArtikelsPerOrder.ArtikelsPerOrderID = qry_ArtikelPerOrderID_EenheidsPrijsBijFranco.ArtikelsPerOrderID) " _
& "LEFT JOIN qry_AantalArtikelTypesPerArtikelPerOrder ON tbl_ArtikelsPerOrder.ArtikelsPerOrderID = qry_AantalArtikelTypesPerArtikelPerOrder.ArtikelsPerOrderID) " _
& "RIGHT JOIN tbl_ArtikelVerwijderdUitZaaglijst ON tbl_ArtikelsPerOrder.ArtikelsPerOrderID = tbl_ArtikelVerwijderdUitZaaglijst.ArtikelsPerOrderID " _
& "GROUP BY DatumNaarWeeknummer([tbl_ArtikelVerwijderdUitZaaglijst]![RegistratieDatum]);"
Set rs = db.OpenRecordset(sql, dbOpenDynaset)
End Sub

(Edit: rearranged to focus on likely culprit)
No, you can't get the last record easily. You can try Select Top 5000 . . . etc., raise the value if it works, and lower the value if it doesn't, and zero in on it that way. But, it is unlikely that a particular is record is causing the problem. I don't think there's bad data somewhere. It's the query.
Focus on the Sum in the select query. Take that out, and you'll likely have the query work. It could well be that the sum overwhelms the numeric type that sql is using to add your values. Actually, the more I think about it, this is probably it. Yeah. If it is, you'll need to force that to a type that can handle larger numbers, like this:
SELECT blah blah, SUM(CAST([TotaalPrijs]/([tbl_ArtikelsPerOrder]![Aantal]*[Totaal])*[tbl_ArtikelVerwijderdUitZaaglijst]![Aantal] AS DECIMAL)) AS GezaagdeOmzet
The syntax might be slightly different for MSAccess, but it will be something like that. Being Access, the default might be int, in which case you might be able to specify Long. Otherwise, specify Decimal. Try to avoid the Real numbers if you can (single, etc.) and they can mess you up if you're not careful.
Though less likely, here are some other possible culprits:
Are you sure this query is logically
correct? This query can be caused by
too large a result set being
returned. Use the Select Top 1000
etc. syntax, and analyze the results
to make sure your joins are working
as you wish, and aren't mistakenly
causing cartesian results, for
example.
If your query is returning legitimate
results, then might it be that the
legitimate result are too massive?
If you really should be getting a
billion results, and this is too
much, then you'll have to change your
whole strategy, or reduce the columns
being returned, etc.

My guess is that this expression:
Sum([TotaalPrijs]/([tbl_ArtikelsPerOrder]![Aantal]*[Totaal])*[tbl_ArtikelVerwijderdUitZaaglijst]![Aantal])
produces an numeric overflow for some records. No knowing what data types your columns use, I can only recommend trying to convert them to a "bigger" data type during the calculation.

Is it possible that the part of the query
([tbl_ArtikelsPerOrder]![Aantal]*[Totaal])*[tbl_ArtikelVerwijderdUitZaaglijst]![Aantal])
Is returning 0? If so that would cause an error, so it might not be large data that is at fault but data that is too small or non-existent

Related

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.

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.

Query by Form for multiple fields in Access 2010

I want to create a query which takes field parameters through a form. For this, I created a form with combo boxes and drop down options to select the values from, this populates a text value in the respective invisible text fields whose default value I have set to null. Now in my query I give criteria for column as iif(isNull([Forms]![Conditions]![text_on_form]), [column_in_table], [Forms]![Conditions]![text_on_form]). I have done this for all the columns on which the where clause comes from the form. I have tried running this. The results seem to be random. It worked for three columns, but when I played around with it, it was giving me empty result set. Can anyone tell me what I am doing wrong? Or if there is a better way to implement query by form in Access.
It sounds like you are trying to create dynamic SQL. Here is the method in vba I generally prefer:
Dim SQL As String
SQL = "SELECT tblName.* From tblName WHERE (1=1)"
If Not IsNull(Me.combo1) Then
SQL = SQL & " And ([Field1] Like ""*" & Me.combo1 & "*"")" ' I am using like statements here, but that is because this is a search tool.
End If
If Not IsNull(Me.combo2) Then
SQL = SQL & " And ([Feild2] Like ""*" & Me.combo2 & "*"")"
End If
Docmd.RunSQL SQL
End Sub
Basically, add on to the SQL statement only if the user has put a value into your text box/ combo box or whatever. The "Where (1=1)" is to account for a situation where all fields are null.
Play with this concept to create your SQL statements. Avoid using invisible text boxes to store data, it generally means you are doing something wrong and will get mixed results (someone else on this forum can explain better than me why that is).
Just use the Like operator. Put this in your criteria field in the query Like "\*" & Forms![Form_Name]![Form_Field] & "\*" -- This tells it to get anything if the field is blank (or null) and matches whatever you have in the field. This may not be what you want. It should be noted that it will return anything with the text string in it. For example: if you type "the" it will return tether, these, theses, thermometer (anything with the word "the" in it. It works best for multi word or longer strings that can be matched more accurately, however it works for a search query because there is usually a set of human eyes looking for the result and erroneous results is not a huge problem.

How to find broken queries in Access?

How do I find broken queries in access.
i.e. Queries that might have broken because the underlying table was deleted or the name of the column in the table changed?
Is there an easy way -- rather than just opening each query running and checking if something has gone wrong?
Here are a few notes that may be of interest, depending on your version of Access.
See: GetDependencyInfo Method [Access 2003 VBA Language Reference]
Do not forget that Track name AutoCorrect info is not a good thing, for the most part, but can be useful in certain circumstances.
Dim dinf As DependencyInfo
For j = 0 To CurrentData.AllQueries.Count - 1
Set dinf = CurrentData.AllQueries(j).GetDependencyInfo
For i = 0 To dinf.Dependencies.Count - 1
''Missing alias, query or table, as far as I can tell
If dinf.Dependencies.Item(i).Name Like "MISSING:*" Then
Debug.Print CurrentData.AllQueries(j).Name _
& " " & dinf.Dependencies.Item(i).Name
End If
Next
Next
You may need to update dependencies:
Application.CurrentProject.UpdateDependencyInfo
This will require a save.

Can I compare two ms-access files? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking us to recommend or find a tool, library or favorite off-site resource are off-topic for Stack Overflow as they tend to attract opinionated answers and spam. Instead, describe the problem and what has been done so far to solve it.
Closed 9 years ago.
Improve this question
I want to compare two ms-access .mdb files to check that the data they contain is same in both.
How can I do this?
I've done this kind of thing in code many, many times, mostly in cases where a local MDB needed to have updates applied to it drawn from data entered on a website. In one case the website was driven by an MDB, in others, it was a MySQL database. For the MDB, we just downloaded it, for MySQL, we ran scripts on the website to export and FTP text files.
Now, the main point is that we wanted to compare data in the local MDB to the data downloaded from the website and update the local MDB to reflect changes made on the website (no, it wasn't possible to use a single data source -- it was the first thing I suggested, but it wasn't feasible).
Let's call MDB A your local database, and MDB B the one you're downloading for comparison. What you have to check for is:
records that exist in MDB A but not in MDB B. These may or may not be candidates for deletion (this will depend on your particular data).
records that exist in MDB B but not in MDB A. These you will append from MDB B to MDB A.
records that exist in both, which will need to be compared field by field.
Steps #1 and #2 are fairly easily accomplished with queries that use an outer join to find the missing records. Step 3 requires some code.
The principle behind the code is that the structure of all the tables in both MDBs are identical. So, you use DAO to walk the TableDefs collection, open a recordset, and walk the fields collection to run a SQL statement on each column of each table that either updates the data or outputs a list of the differences.
The basic structure behind the code is:
Set rs = db.OpenRecordset("[SQL statement with the fields you want compared]")
For Each fld In rs.Fields
' Write a SQL string to update all the records in this column
' where the data doesn't match
strSQL = "[constructed SQL here]"
db.Execute strSQL, dbFailOnError
Next fld
Now, the major complexity here is that your WHERE clause for each field has to be different -- text fields need to be treated differently from numeric and data fields. So you'll probably want a SELECT CASE that writes your WHERE clause based on the field type:
Select Case fld.Type
Case dbText, dbMemo
Case Else
End Select
You'll want to use Nz() to compare the text fields, but you'd use Nz(TextField,'') for that, while using Nz(NumericField,0) for numeric fields or date fields.
My example code doesn't actually use the structure above to define the WHERE clause because it's limited to fields that work very well comparing concatenated with a ZLS (text fields). What's below is pretty complicated to read through, but it's basically an expansion on the above structure.
It was written for efficiency of updates, since it executes a SQL UPDATE for each field of the table, which is much more efficient than executing a SQL UPDATE for each row. If, on the other hand, you don't want to do an update, but want a list of the differences, you might treat the whole thing differently. But that gets pretty complicated depending on the output,
If all you want to know is if two MDBs are identical, you would first check the number of records in each table first, and if you have one non-match, you quit and tell the user that the MDBs aren't the same. If the recordcounts are the same, then you have to check field by field, which I believe is best accomplished with column-by-column SQL written dynamically -- as soon as one of the resulting SQL SELECTS returns 1 or more records, you abort and tell your user that the MDBs are not identical.
The complicated part is if you want to record the differences and inform the user, but going into that would make this already-interminable post even longer!
What follows is just a portion of code from a larger subroutine which updates the saved query qdfOldMembers (from MDB A) with data from qdfNewMembers (from MDB B). The first argument, strSQL, is a SELECT statement that is limited to the fields you want to compare, while strTmpDB is the path/filename of the other MDB (MDB B in our example). The code assumes that strTmpDB has qdfNewMembers and qdfOldMembers already created (the original code writes the saved QueryDef on the fly). It could just as easily be direct table names (the only reason I use a saved query is because the fieldnames don't match exactly between the two MDBs it was written for).
Public Sub ImportMembers(strSQL As String, strTmpDB As String)
Const STR_QUOTE = """"
Dim db As Database
Dim rsSource As Recordset '
Dim fld As Field
Dim strUpdateField As String
Dim strZLS As String
Dim strSet As String
Dim strWhere As String
' EXTENSIVE CODE LEFT OUT HERE
Set db = Application.DBEngine(0).OpenDatabase(strTmpDB)
' UPDATE EXISTING RECORDS
Set rsSource = db.OpenRecordset(strSQL)
strSQL = "UPDATE qdfNewMembers INNER JOIN qdfOldMembers ON "
strSQL = strSQL & "qdfNewMembers.EntityID = qdfOldMembers.EntityID IN '" _
& strTmpDB & "'"
If rsSource.RecordCount <> 0 Then
For Each fld In rsSource.Fields
strUpdateField = fld.Name
'Debug.Print strUpdateField
If InStr(strUpdateField, "ID") = 0 Then
If fld.Type = dbText Then
strZLS = " & ''"
Else
strZLS = vbNullString
End If
strSet = " SET qdfOldMembers." & strUpdateField _
& " = varZLStoNull(qdfNewMembers." & strUpdateField & ")"
strWhere = " WHERE " & "qdfOldMembers." & strUpdateField & strZLS _
& "<>" & "qdfNewMembers." & strUpdateField & strZLS _
& " OR (IsNull(qdfOldMembers." & strUpdateField _
& ")<>IsNull(varZLStoNull(qdfNewMembers." _
& strUpdateField & ")));"
db.Execute strSQL & strSet & strWhere, dbFailOnError
'Debug.Print strSQL & strSet & strWhere
End If
Next fld
End If
End Sub
Code for function varZLSToNull():
Public Function varZLStoNull(varInput As Variant) As Variant
If Len(varInput) = 0 Then
varZLStoNull = Null
Else
varZLStoNull = varInput
End If
End Function
I don't know if that's too complex to make sense, but maybe it will help somebody.
You can try AccessDiff (paid product). It has the ability to compare the schema, the data, and also access objects. It has a GUI and also a command line interface.
Disclosure: I am the creator of this tool.
Take text dumps of database tables and simply compare the dumped text files using BeyondCompare (or any other text comparison tool). Crude but can work!
I have very good experience with Cross-Database Comparator. It is able to compare structure and/or data.
See the Compare Access databases section at the Microsoft Access third party utilities, products, tools, modules, etc. page at my website.
I've added "table diff" feature to my accdbmerge utility not so long time ago.
I beleive that this answer will not help to solve original question, but it may be helpful for someone faced with the same problem in the future.
If you want to know if the files are identical then
fc file1.mdb file2.mdb
on a DOS command line.
If the files aren't identical but you suspect they contain the same tables and records then the easiest way would be quickly write a small utility that opens both databases and cycles through the tables of both performing a heterogeneous query to extract the Diff between the two files.
There are some tools out there which will do this for you, but they all appear to be shareware.