Why do I get multiple results from a MAX SQL query? - sql

In Access, I have a table named tblBundle and am trying to query the last step that was completed on every bundle in an order.
tblBundle contains: Work Order, Bundle #, BundleLtr, Step, Complete [boolean]
I ultimately want to copy this into vba to create a list of the Bundle #, Ltr, and last step completed for continued data entry on the next step completed.
For some reason I cannot ascertain, the SQL query below returns 2 records for bundle 1 (190, 200) when I believe it should only be returning one record - 200.
SELECT Max(tblBundle.Step) AS intLstep, tblBundle.BundleNbr, tblBundle.BundleLtr,
tblBundle.Complete
FROM tblBundle
GROUP BY tblBundle.WO, tblBundle.BundleNbr, tblBundle.BundleLtr, tblBundle.Complete
HAVING (((tblBundle.WO)="195687-1-1") AND ((tblBundle.Complete)=True));
Can anyone help me figure out why my query is returning the extra value?

In MsAccess, NULL values appear as blanks when viewed on the Datasheet view. Also, it seems that when blanks are entered for a ShortText column on the Datasheet view, they are turned into NULLs (on my version of MsAccess). It seems that your data may have NULL for one of the Steps (say 200), and spaces (zero or more) for the other (say Step 190), you could force your query to treat both the same way:
SELECT Max(tblBundle.Step) AS intLstep, tblBundle.BundleNbr,
nz(tblBundle.BundleLtr,'') as BundleLtr,
tblBundle.Complete
FROM tblBundle
GROUP BY tblBundle.WO, tblBundle.BundleNbr,
nz(tblBundle.BundleLtr,''), tblBundle.Complete
HAVING (((tblBundle.WO)="195687-1-1") AND ((tblBundle.Complete)=True));
Please note that the invisible character can, in fact, be a character that looks like space, but it is not (like non-breaking space), etc. If the above solution does not work, try something like:
SELECT asc(mid(BundleLtr, 1,1)), asc(mid(BundleLtr, 2,1)) FROM Table2 where Step=190
To see the ascii values of the data in that column.

It seems like because your query is breaking down the the result and also returning BundleNbr is causing it to take both Max because its valid.
Also you break this down in your Group By statement and aggregate by that.
Both values of 190 and 200 have a BundleNbr of 1. If you remove that column you'll get the actual max value of 200 (If you want the max of intLStep.

I was able to use this following function to "clean up" my string and remove the additional 'Bundle 1'. This does not solve the original question, but works for my purposes.
'[Removes duplicate values from a string]
Function DeDupeString(ByVal sInput As String, Optional ByVal sDelimiter As
String = ",") As String
Dim varSection As Variant
Dim sTemp As String
For Each varSection In Split(sInput, sDelimiter)
If InStr(1, sDelimiter & sTemp & sDelimiter, sDelimiter & varSection & sDelimiter, vbTextCompare) = 0 Then
sTemp = sTemp & sDelimiter & varSection
End If
Next varSection
DeDupeString = Mid(sTemp, Len(sDelimiter) + 1)
End Function
Found From: Removing Duplicate values from a string in VBA

Related

How to count and output rows in Access VBA [duplicate]

I am using DCount to help display an error message if the data signals an error. I have it working with one criteria where a number is equal to another number. Now I wanted to add another criteria in there where another field, ReturnDate (this field is Text not Date/Time) is equal to a hyphen (-).
I'm just not really sure how to format it. I have this:
If DCount("*", "CrewTable", "KitNumber=" & _
Me.AssignKit.Value And "ReturnDate=" & _
"-") > 0 Then
Cancel = True
MsgBox "Kit is already assigned!"
AssignKit = ""
AssignKit.SetFocus
Else
...
The error pops up with a 'Type Mistmatch' and the debugger highlights the whole statment from 'If -> Then' and has an error pointing to the line with the hyphen in the quotes.
If DCount("*", "CrewTable", "ReturnDate='-' AND KitNumber=" & _
Me.AssignKit.Value) > 0 Then
It's easier to troubleshoot DCount errors when you store its Criteria option in a string variable.
Dim strCriteria As String
strCriteria = "ReturnDate='-' AND KitNumber=" & Me.AssignKit.Value
Debug.Print strCriteria
If DCount("*", "CrewTable", strCriteria) > 0 Then
If you had used this approach, Access would have alerted you to the fact that the original code which built the Criteria string was invalid. That should make it clearer that the problem wasn't due to the If condition, and it wasn't exactly a DCount problem either ... it was a problem with string concatenation.
Me.AssignKit.Value & " And ReturnDate=" & _

Autofilter does not display existing data

I am using VBA to autofilter a large list. (Just under 5,000 rows). Specifically, I am searching for a vendor in an expense log. When I run this code, it finds many vendors just fine, but not others.
Private Function testMcTesterson()
Dim icell As Range
Dim tempStr As String
Dim i As Integer
Dim w As Workbook
Dim expLog As Workbook
Dim vendorName As String
Set expLog = Workbooks("FY18 Manual Expense Log.xlsm")
Set w = ActiveWorkbook
For Each icell In Selection
vendorName = VendorNormalizer(icell.Value)
expLog.Activate
'Filter by vendor
Debug.Print "Vendor name is " & "'" & vendorName & "'"
ActiveSheet.Range("A1").AutoFilter field:=5, Criteria1:=vendorName
w.Activate
Next icell
End Function
I am calling another function I have built to normalize the names of the vendors. Several are not working (and several are), but the one I am wrestling with at the moment is Amazon. We have several transactions from Amazon, but none show.
Things I have checked so far:
Type the vendor's name into the autofilter. This produces results, so it is not the case that rows are excluded from the filter.
Check which columns are autofiltered. Only the vendor column, so the code is not applying the filter to the wrong column. (Also because it works for other vendors)
I was suspecting the problem may be with the output of my normalizing function, so I inserted my debug.print statement. Immediately before the filter is run, the console reads "Vendor name is 'Amazon'" No spaces before or after. I copied that directly from my immediate window, in case I just spelled something wrong.
I even checked typename(vendorName) and it returns string.
I'm sure the solution is right in front of my face, I just can't see it.
Help!
Edit: Here is the pertinent code for VendorNormalizer
Private Function VendorNormalizer(vendorName As String)
Select Case True
'Lots of other cases
Case InStr(1, vendorName, "Amazon", vbTextCompare) > 0
VendorNormalizer = "Amazon"
'Lots of other cases
End select
'If statements for exact matches
End function
The spreadsheet I am searching has all of these (and no other Amazon entries) in the column I am filtering. (copied and pasted)
AMAZON.COM
Amazon marketplace
Amazon.com
AMAZON MKTPLACE PMTS
Thanks to TotsieMae for having the breakthrough suggestion. Turns out wildcards are the answer.
Apparently the VBA range.autofilter command requires the criteria be an exact match, though typing into the autofilter does not. The solution is as simple as including wildcards in the text output of the normalizer.
To:
Select Case True
'Lots of other cases
Case InStr(1, vendorName, "Amazon", vbTextCompare) > 0
VendorNormalizer = "*Amazon*"
'Lots of other cases
End select
Thank you all for your help!

VBA Word table with unknown number of fused rows/columns

I'm currently trying to work with complex tables in Microsoft Word. My problem is, those tables have fused cells and rows, and I'm not sure of how many rows or columns i'll have.
Here is a (stupid) example how the kind of tables i'll have
I get my table thanks to a bookmark, and then proceed to stock the table in a Dim for easier access
Sub SetTable()
Dim tb as Table
Selection.GoTo What:=wdGoToBookmark, Name:="MyTable"
Selection.MoveDown
Set tb = Selection.Tables(1)
End Sub
Now, I'd use that table to write in several tables of a database.
Let's say, I have a table "Destinations", a table "Ways" and a table "Time"
I'm kinda blocked there.
With fused rows and columns, i cannot access a whole column or row. But as i don't know how many rows and columns i have (i could have, for example, 5 different ways for "Destination 1", or several distances in "Way 1")
I am a little lost on how i should try to work.
Cell(x,y).Row doesn't work because several rows are fused, and it is the same with Column, so we get errors extremely easily
I was thinking of putting tables in cells that might get an unknown number of rows/columns, a bit like this
The Problem with this method is that the person that'll write in the document won't be me. Meaning, if he has to create a table each time there is a new line/column that requires it, chance is that it'll become a problem quickly.
(I haven't found yet a method to put something in a given cell of a table at the creation of a new line, I'm also open on that point)
I was wondering if there are best practices to apply in this kind of case, and I am looking for advices too.
If you already had to treat something similar to this, how did you do?
Thanks in advance for your answers
Cordially,
Zawarudio
Note : The example of table here is insanely stupid, and even I don't even know what it's talking about. It was just to put informations in the tables, and have absolutely no link with what I'm trying to do.
If you were lost by the distances/times/whatever, sorry about that
I had some vacations so I didn't work on that question before now.
I just found a way that I felt was relevant, so I come here to share my answer
Note that I only worked on an unknown number of merged rows at the moment, so this answer will only be about that, though I believe it is the same. Also note that I'm on Word 2010. I don't know if rows/column behavior changed in 2013 or will change in the future. (well, obviously)
The big problem was that a merged row cell will only have a value of the first row of the merged row. Let's take a simple example
This table has 2 rows and 2 columns. We fused the rows of the 1st column.
table.Rows.Count will return 2, so will table.Columns.count.
table.cell(1,1).Range.text will return the content of the merged rows.
We would like table.cell(2,1).Range.text to return the value of the merged row, but VBA tells us here that this value doesn't exist.
There is no problem with table.cell(1,2).Range.text and table.cell(2,2).Range.text.
With values, that means that our table with merged rows is pretty equals to that
Where each empty cell would generate an error 5941.
How to resolve the problem?
Sub ReadAllRows()
Dim NbRows As Integer
Dim NbColumns As Integer
Dim i, j As Integer
Dim SplitStr() As String
Dim col1 as String
Dim col2 as String
Dim col3 as String
Dim col4 as String
'note : my table here is a public value that i get thanks to bookmarks
NbRows = table.Rows.count
NbColumns = table.Columns.count
For i = 3 To NbRows
'We put each value of each columns in a dim
'We do that to remember previously entered row value if the application encounters an error
'Because of merged rows, some cells on each row will not exist and return an error
'When the application encounters an error, it just proceeds to next column
'As previous existing value of this column was stocked in a Dim, we can get the full row at the end of the column loop
For j = 1 To NbColumns
On Error GoTo ErrorHandler
SplitStr = Split(table.Cell(i, j).Range.Text, Chr(13))
Select Case j
Case 1:
col1 = SplitStr(0)
Case 2:
col2 = SplitStr(0)
Case 3:
col3 = SplitStr(0)
Case 4:
col4 = SplitStr(0)
'ect...
End Select
NextRow:
Next j
'We have here all the values of the line
MsgBox "col1: " & col1 & Chr(10) & _
"col2: " & col2 & Chr(10) & _
"col3: " & col3 & Chr(10) & _
"col4: " & col4 & Chr(10)
Next i
'This Error handler will skip the whole Select Case and thus will proceed towards next cell
ErrorHandler:
If Err.Number = 5941 Then
Err.Clear
Resume NextRow
End If
End Sub
That way, when a cell doesn't exist, that mean the row if merged. Meaning we want the last known value of the row. Since we skip the whole select when row is unknown, the value of the Dim isn't changed while we do get right the value of not merged rows.
This isn't rocket science, but I first began with a simple On Error Resume Next, and with that, non-existing rows simply had the value of last existing row, so I also had to work on a function that would try to get the good value for each cell of each row...
Note that I did things the ugly way here, but you can use a one dimensionnal arrays to stock an entire row the way Word is supposed to understand it, or you can even get a two dimensionnal array stocking your whole table in it a way Word understands
Well, I hope it helps someone, someday!
Cordially,
Zawarudio
I think there must be an existing Q/A about this but I didn't find it using a quick search, so for now...
One thing you can do is iterate through the cells of the range of the table. Like this:
Sub iterTable()
Dim r As Range
Set r = ActiveDocument.Tables(1).Range
For i = 1 To r.Cells.Count
Debug.Print r.Cells(i).RowIndex, r.Cells(i).ColumnIndex, r.Cells(i).Range.Text
Next
End Sub
As long as you have predefined texts that will allow you to detect your "Destination" groups, that should be enough for you to make progress...

How to parse this specific type of data in excel VBA?

I have a bunch of different sets of engineering measurements in the format:
77.170 (+/- 0.025)
And I need to split it into the first number, which is the nominal value, and the number in the parenthesis, the tolerance. Not sure exactly how to do this in excel VBA. I was thinking I would use the Split function with a space delimiter, giving me the first number, then the unnecessary characters, then the tolerance, but the tolerance will include a parenthesis. How could I get rid of just that parenthesis, and will what I just suggested even work? Thanks!
Consider:
Sub dural()
s = "77.170 (+/- 0.025)"
s2 = Replace(Replace(Replace(s, " ", ""), "+/-", ""), ")", "")
ary = Split(s2, "(")
MsgBox ary(0) & vbCrLf & ary(1)
End Sub
Use Text to Columns and a formula.
Go to Data--->Text to Columns. Choose delimited and choose Space as your delimiter. This should split the text string into something like:
ColA |ColB|ColC
77.170|(+/-|0.025)
Column C is a bit funky, so let's just grab everything but the last character.
In column D put this:
=LEFT(C1,LEN(C1)-1)
Finally, you should get:
ColA |ColB |ColC |ColD |
77.170|(+/- |0.025)|0.025|
I would use a combination of instr() and mid to get what you need. For example
measurments="77.170 (+/- 0.025)"
mid(measurements,1,instr(measurements," "))
trim(mid(measurements,instr(measurements,"-")+1,instr(measurements,")")-instr(measurements,"-")-1))
or, to combine,
measurments="77.170 (+/- 0.025)"
mid(measurements,1,instr(measurements," ")) & " " & trim(mid(measurements,instr(measurements,"-")+1,instr(measurements,")")-instr(measurements,"-")-1))
Try using a combination of InStr(), Left(), Right().
Get the index/position of the '(' using InStr and then extract the characters using Left and Right. If you want to get the final data as a double or a Long use CDbl() or CLng() respectively.
For getting text out of other text consider using Regular Expresions.
To use them in VBA you will need in Reference 'VBScript_RegExp_55' library.
The reason why you might want to do that is because following code returns whatever first two numbers show up in your text( it can be modified to be much smarter than that), regardless of other text around it.
Dim Regex As RegExp
Dim Matches As MatchCollection
Set Regex = New RegExp
Regex.Pattern = "\d*\.\d*"
Regex.Global = True
Set Matches = Regex.Execute("77.170 (+/- 0.025)")
MsgBox (Matches(0).Value & " " & Matches(1).Value)
Assuming s is your measurement string, here is the most direct way:
v = Split(Left(s, Len(s) - 1), " (+/- ")
That's it. Now v(0) holds the nominal value and v(1) holds the tolerance.

How do I delete all characters after the first space in a cell?

I have a list of city names followed by the state in which they are located all in one column in Excel. How can I delete everything after the first space so that the city name is the only thing that's left in the cell?
example: A1 = "johnson TX"
should be just A1= "johnson"
I assume you want a VBA solution since you tagged your question excel-vba.
This works:
Sub KeepCity()
Dim strCityAndState As String
Dim strCityOnly As String
strCityAndState = Range("A1").Value
strCityOnly = Left(strCityAndState, InStr(strCityAndState, " ") - 1)
Range("A2").Value = strCityOnly
End Sub
If you don't want VBA and want a cell formula instead, then #JToland's answer works fine, though this one is more concise and doesn't keep the trailing space character:
=LEFT(A1, FIND(" ",A1)-1)
Well doing something like this
=Mid(A1, 1, Find(" ",A1))
in another column should grab all text before the " ". So you could build another column with just all the city names and then you could use that column for your purposes.
If you are looking for a VBA function, you can use Left and InStr as shown below.
Dim Temp As String: Temp = "Hello_World! This is my first answer here. :D"
Temp = Left(Temp, InStr(Temp, " ")-1)
In which case, Temp will be "Hello_World!"
Use of Split function
An elegant approach is to use the first token of the Split function:
Code Example extracting from cell A1 to A2
Option Explicit
Sub KeepCity()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("MySheet") ' << Change to your sheet name
ws.[A2] = Split(ws.[A1], " ")(0)
End Sub
Alternative Syntax
Instead of cell abbreviations [A2] or [A1] you can also use:
ws.Range("A2").Value = Split(ws.Range("A1").Value, " ")(0)
Note
The resulting split 1-dimensional array is zero based, so you get the first part (or token) of the original string via index (0).
If you are looking for a second part, I recommend adding an additional delimiter value (" ") to the original string, e.g. s: MsgBox split(s & " "," ")(1). By that way you avoid error number 9 "Subscript out of range", if there is no delimiter in between at all, thus no second part at all.