inner loops, procedure call confusion - vb.net

So what the issue is, i have 2 for loops one nested within another. The outer loop calling a procedure, the inner loop setting a attribute for the procedure to use. The problem is that the procedure is that I wan't to exit the loop use the pos <-- attribute call the procedure and re-enter the inner loop. At the minute the pos is only being set once because all the conditions are true within the inner loop meaning that its being replaced each time. I want to be able to set the pos exit the inner loop, call the procedure and re-enter the inner loop and set it pos to a different value? any help would be great!! here is the code
For Each val As String In vals
If creditPoints = "20" And semester = "1" And year = "Year 1" Then
For Each position In MyPosList
If position.strLabel = "a1" And available(0) <> "False" Then
pos = position.strX & " " & position.strY
count += 1
available(0) = blnavailable
ElseIf position.strLabel = "b1" And available(1) <> "False" Then
pos = position.strX & " " & position.strY
count += 1
available(1) = blnavailable
Next
shortfat(semester, pos, creditPoints, title, year, modStatus, count)
End If
next

Are you just looking to break out of the inner loop when one of those conditions are met? If so, that's what the Exit keyword is for. You really should also set a flag to sanity check yourself, too.
''//Flag so that we know if we actually found a position
Dim FoundPosition as Boolean
For Each val As String In vals
''//Reset the flag and assume that are conditions are met
FoundPosition = False
If creditPoints = "20" And semester = "1" And year = "Year 1" Then
For Each position In MyPosList
If position.strLabel = "a1" And available(0) <> "False" Then
pos = position.strX & " " & position.strY
count += 1
available(0) = blnavailable
''//Flag that our conditions are met
FoundPosition = True
''//Exit from the inner loop
Exit For
ElseIf position.strLabel = "b1" And available(1) <> "False" Then
pos = position.strX & " " & position.strY
count += 1
available(1) = blnavailable
''//Flag that our conditions are met
FoundPosition = True
''//Exit from the inner loop
Exit For
End If
Next
''//Sanity check to ensure that our conditions are met
If FoundPosition Then
shortfat(semester, pos, creditPoints, title, year, modStatus, count)
Else
''//Do something here, either Throw an error or safely handle this case otherwise
End If
End If
Next

"I want to be able to set the pos exit the inner loop, call the
procedure and re-enter the inner loop and set it pos to a different
value?"
Things like that are better served with a WHILE loop than a FOR loop.
Just a stab in the dark here, but it sounds like you want to be able to see if shortfat is producing a favorable value. And if it does not, you want it to recompute pos. The first thing I'd do, is alter shortfat to return some kind of value...for my example, I'll have it return a boolean.
Dim blnDidThisDoWhatIWant As Boolean
For Each val As String In vals
If creditPoints = "20" And semester = "1" And Year() = "Year 1" Then
blnDidThisDoWhatIWant = False
While blnDidThisDoWhatIWant = False
For Each position In MyPosList
If position.strLabel = "a1" And available(0) <> "False" Then
pos = position.strX & " " & position.strY
count += 1
available(0) = blnavailable
ElseIf position.strLabel = "b1" And available(1) <> "False" Then
pos = position.strX & " " & position.strY
count += 1
available(1) = blnavailable
End If
Next
blnDidThisDoWhatIWant = shortfat(semester, pos, creditPoints, title, Year, modStatus, count)
End While
End If
Next
This will allow you to re-enter the inner loop. The problem is that it'll be infinite unless pos is computed differently (which I don't see how it can). So that's something you'll have to work out on your own. Hope this helps point you in the right direction.

Related

Is there a way to optimize nested if clauses?

for a while now I've tried to solve the decreased speed issue in my Access application when opening a print preview of certain reports. I've noticed that the slow reports have one thing in common - long, nested if clauses. I tried to search the internet for an answer for this issue, but some of the solutions do not apply to Access VBA or they just aren't possible to implement in the case of my application.
I was wondering if there are some commonly known ways that are used in order to avoid if clause monsters?
EDIT: A snip of code - it mostly handles the structure of the report based on certain conditions.
If (strCcDocNumber <> vbNullString) Then
Dim strUpperPart As String, strLowerPart As String
IDModule.placeIDStringsToPrivateVariables strCcDocNumber, ", "
strUpperPart = IDModule.returnUpper()
strLowerPart = IDModule.returnLower()
txtIDs = strUpperPart & vbCrLf & strLowerPart
Else
txtIDs = " " & vbCrLf & " "
End If
If (strOrderNumber = IO_OrderNumber.OrderNumberCode & "12345") Then
txtIDs = txtIDs
txtIDSpec1 = ModuleIDSpec1.getIDSpec1
txtIDSpec2 = ModuleIDSpec2.getIDSpec2
txtIDSpec1.Height = 330
txtIDSpec2.Height = 330
txtUpperLower = "- Ex" & vbCrLf & "- Ex2" & vbCrLf & vbCrLf & "- Ex3"
On Error Resume Next
For Each c In Me.Controls
If (c.Tag = "IDSpec2Table" Or c.Tag = "IDSpec1Table") Then c.Height = 0
If (c.Tag = "IDSpec2Table" Or c.Tag = "IDSpec1Table") Then c.Visible = False
If (c.Tag = "IDSpec2Table" Or c.Tag = "IDSpec1TableExtra") Then c.Height = 0
If (c.Tag = "IDSpec2Table" Or c.Tag = "IDSpec1TableExtra") Then c.Visible = False
If (c.Tag = "IDSpec2Texts" Or c.Tag = "IDSpec1Texts") Then c.Visible = True
If (c.Tag = "IDSpec2Texts" Or c.Tag = "IDSpec1Texts") Then c.Height = 330
If (c.Tag = "IDSpec2Texts" Or c.Tag = "IDSpec1TextsExtra" And ModuleTarget.TargetGroup <> "23C") Then c.Visible = True
If (c.Tag = "IDSpec2Texts" Or c.Tag = "IDSpec1TextsExtra" And ModuleTarget.TargetGroup <> "23C") Then c.Height = 330
'+ many more tags
Next
On Error GoTo 0
txtIDSpec1.Visible = True
txtIDSpec2.Visible = True
If (txtIDSpec1 = vbNullString And txtIDSpec2 = vbNullString) Then
txtIDSpec1.Height = 0
txtIDSpec2.Height = 0
txtIDSpec1.Visible = False
txtIDSpec2.Visible = False
End If
Else
'+a lot more similar conditions
EDIT: I remembered which if statements were the most troublesome ones. I think you can't change these ones into select cases or ElseIf statements, because all of the conditions need to be checked...
It goes like this:
If (condition) Then
Do this
If (differentCondition) Then
Do this also
If (completelyDifferentCondition) Then
Do this as well
Else
Do this instead
End If
End If
Else
If (yetAnotherCondition) Then
Do this
Else
Do this instead
End If
End If
I was wondering if there are some commonly known ways that are used in
order to avoid if clause monsters?
First step is to work out what you want to achieve, not how you want to do it. In this context, you want to set height and visibility. From here, you can work out what conditions are required to set this.
When you first do this, you will have some monster clauses - but this is OK because you have not clarified your thinking. Work from an assumption of one state unless proven otherwise. Here is an example:
c.visible = True
If ((c.Tag = "IDSpec2Table" Or c.Tag = "IDSpec1Table") OR (c.Tag = "IDSpec2Table" Or c.Tag = "IDSpec1TableExtra")) then c.visible = True
Of course, the second line can now be simplified a little bit.
If (c.Tag = "IDSpec2Table" Or c.Tag = "IDSpec1Table" Or c.Tag = "IDSpec1TableExtra") then c.visible = True
I also I set marker Booleans - for example:
IsSpecTable = (c.Tag = "IDSpec2Table" Or c.Tag = "IDSpec1Table")
IsMySpecialFriend = (c.Tag = "IDSpec1TextsExtra" And ModuleTarget.TargetGroup <> "23C")
[...]
c.Visible = IsSpecTable Or IsMySpecialFriend
These are a couple of techniques I use to simplify complex business logic. I am also looking at the use of flags, but this means converting the text Tag to an enumerated value (I am doing this in VB.Net). This technique, though, simplifies the expression down to a simple mask with a And or Or operator as appropriate.
Consider using Select Case Statements when you have multiple If Statement based off the same value.
MSDN - Select Case Statement
Executes one of several groups of statements, depending on the value of an expression.
For Each c In Me.Controls
Select Case c.Tag
Case "IDSpec2Table", "IDSpec1Table", "IDSpec1TableExtra"
c.Height = 0
c.Visible = False
Case "IDSpec2Texts", "IDSpec1Texts"
c.Visible = True
c.Height = 330
Case "IDSpec1TextsExtra"
If ModuleTarget.TargetGroup <> "23C" Then
c.Visible = True
c.Height = 330
End If
End Select
Next
Performance: Select Case vs If vs If ElseIf
I mentioned in a comment that using a Select Case is more for readability than performance. Which is correct if we are comparing Select Case and If ElseIf statements (read Which way is faster? If elseif or select case).
Select Case and If ElseIf can be considerably faster than multiple If statements. This is because the VBA evaluates every condition in an If statement and will stop evaluating when one condition is meet in the Select Case statement. Note: not all languages do.
Consider this simple test.
Sub Test()
Debug.Print "Test:If Statement Test:"
If ret(1) = 1 Or ret(2) = 2 Or ret(3) = 3 Or ret(4) = 4 Or ret(5) = 5 Then
End If
Debug.Print vbNewLine; "Test:If ElseIf Statement"
If ret(1) = 1 Or ret(2) = 2 Then
ElseIf ret(3) = 3 Then
ElseIf ret(4) = 4 Then
ElseIf ret(5) = 5 Then
End If
Debug.Print vbNewLine; "Test:Select Case Statement"
Select Case 1
Case ret(1), ret(2)
Case ret(3)
Case ret(4)
Case ret(5)
End Select
End Sub
Function ret(n As Long) As Long
ret = n
Debug.Print n,
End Function
Notice that the If statement had to perform 5 operations even though they were all true. The ElseIf had to perform 2 operations because the first 2 operations were grouped in a single If clause. The Select Case only performed a single operation, even though, two operations were grouped together. This is because the Select Case will always stop evaluating conditions when a single condition is true.

Is it possible to populate a Farpoint Spread 6.0 vaSpread component using a SQL query in VB6?

I have written a query using T-SQL on SQL Server 2008 R2 that provides the correct information that I need to display on a vaSpread component named SSlist on Visual Basic 6. I have already opened the connection to the database, but I am having difficulty finding resources on how to populate the vaSpread component directly using my T-SQL query. I just need to display it exactly as how it shows up when I execute it in Microsoft SQL Server Management Studio.
My query is:
SELECT QC.LINE_CD AS 'Line Code', QC.LINE_NM AS 'Line Name', PN.GUBUN, WO.WRK_QTY AS 'Work QTY', CM.LINE_TARGET AS 'Line Target',
CM.RETURN_TARGET AS 'Return Target', SUM(PN.R_QTY) AS 'Rework QTY', SUM(PN.S_QTY) AS 'Scrap QTY',
SUM(PN.UPRC_AMT) AS 'UPRC AMT', (SUM(COALESCE(PN.UPRC_AMT,0)*PN.S_QTY)+SUM(PN.R_QTY)*3.8) AS 'Cost'
FROM QC_LINE_MST AS QC
LEFT JOIN (SELECT PE.LINE_CD, PE.WRK_YMD, PE.CUST_CD, PE.GUBUN, PE.ITMNO, PE.R_QTY, PE.S_QTY, ND.UPRC_AMT FROM PROC_ERR AS PE
INNER JOIN (SELECT ITMNO, CUST_CD, UPRC_AMT FROM NOW_DANGA) AS ND ON PE.ITMNO = ND.ITMNO AND PE.CUST_CD = ND.CUST_CD
WHERE PE.WRK_YMD BETWEEN '20161116' AND '20161201' AND (PE.R_QTY <> 0 OR PE.S_QTY <> 0)
) AS PN ON QC.LINE_CD = PN.LINE_CD
LEFT JOIN (SELECT A.CODE, A.DSCP AS LINE_TARGET, B.DSCP AS RETURN_TARGET FROM COD_MST AS A
INNER JOIN (SELECT CODE, DSCP FROM COD_MST WHERE GUBN='QC09'
) AS B ON A.CODE = B.CODE
WHERE A.GUBN='QC08') CM ON QC.LINE_CD = CM.CODE
LEFT JOIN (SELECT LINE_CD, SUM(WRK_QTY) AS WRK_QTY FROM WRK_ORD
WHERE WRK_YMD BETWEEN '20161116' AND '20161201' GROUP BY LINE_CD
) AS WO ON QC.LINE_CD = WO.LINE_CD
GROUP BY QC.LINE_CD, QC.LINE_NM, WO.WRK_QTY, PN.GUBUN, CM.LINE_TARGET, CM.RETURN_TARGET
ORDER BY QC.LINE_CD
I've searched online trying to figure out how to populate my vaSpread using this query, but either I am looking in the wrong place, or resources on Farpoint Spread 6.0 are scarce. If anyone has any ideas on how to implement this, or could direct me towards some helpful literature it would be much appreciated. Also, if anyone has any ideas on how to clean up my SQL
query and make it more efficient, that's welcome as well. I'm pretty new to this. Thank you, and let me know if I need to provide any more information! I look forward to reading your suggestions.
After doing some more research, I learned that instead of using the FarPoint 6.0 vaSpread component (non-OLEDB), the FarPoint 6.0 FpSpread component (OLEDB capable) should be used in order to automatically populate the spread sheet. However, the method to automatically populate the new FpSpread component required:
1) an ADODC component connected to the database
2) a stored procedure on the database
Seeing as how I already had an active connection and also needed to use certain column records in calculations to populate other columns, I decided to go with a manual spreadsheet population method using FOR loops. My code is attached below so that anyone having any similar issues can use my code for ideas.
With SSlist
//SQL Query to USA_ERP.QC_LINE_MST Table to receive total number of Rows in Record Set
SqlStmt = CSQL("SELECT COUNT(*) AS 'Count' FROM QC_LINE_MST")
Rs.Open SqlStmt, CN, adOpenForwardOnly, adLockReadOnly
LastRow = Val(Rs.Fields("Count"))
RowB4Last = Val(Rs.Fields("Count")) - 1
.MaxRows = LastRow
Rs.Close
//Formatting for Last Row (Totals row)
For RowCount = 1 To LastRow
.Row = RowCount
.RowHeight(.Row) = 18
//Font and cell formatting for Line Columns
For ColCount = 1 To 1
.Col = ColCount
.CellType = CellTypeStaticText
.TypeHAlign = TypeHAlignCenter
.FontBold = True
.TypeVAlign = TypeVAlignCenter
Next
If .Row = LastRow Then
//Merge for Totals label of Last Row (Totals row)
For ColCount = 1 To 2
.Col = ColCount
.Text = "Totals"
.RowMerge = MergeRestricted
Next
//Font and cell formatting for Last Row (Totals row)
For ColCount = 1 To 15
.Col = ColCount
.CellType = CellTypeStaticText
.TypeHAlign = TypeHAlignCenter
.FontBold = True
.TypeVAlign = TypeVAlignCenter
Next
End If
Next
//Main SQL Query to USA_ERP Database
SqlStmt = CSQL("SELECT QC.LINE_CD AS 'Line Code', QC.LINE_NM AS 'Line Name', PN.GUBUN, WO.WRK_QTY AS 'Work QTY', CM.LINE_TARGET AS 'Line Target', " & _
"CM.RETURN_TARGET AS 'Return Target', SUM(PN.R_QTY) AS 'Rework QTY', SUM(PN.S_QTY) AS 'Scrap QTY', " & _
"SUM(PN.UPRC_AMT) AS 'UPRC AMT', (SUM(COALESCE(PN.UPRC_AMT,0)*PN.S_QTY)+SUM(PN.R_QTY)*3.8) AS 'Cost' " & _
"FROM QC_LINE_MST AS QC " & _
"LEFT JOIN (SELECT PE.LINE_CD, PE.WRK_YMD, PE.CUST_CD, PE.GUBUN, PE.ITMNO, PE.R_QTY, PE.S_QTY, ND.UPRC_AMT FROM PROC_ERR AS PE " & _
"INNER JOIN (SELECT ITMNO, CUST_CD, UPRC_AMT FROM NOW_DANGA) AS ND ON PE.ITMNO = ND.ITMNO AND PE.CUST_CD = ND.CUST_CD " & _
"WHERE PE.WRK_YMD BETWEEN '$S' AND '$S' AND (PE.R_QTY <> 0 OR PE.S_QTY <> 0) " & _
") AS PN ON QC.LINE_CD = PN.LINE_CD " & _
"LEFT JOIN (SELECT A.CODE, A.DSCP AS LINE_TARGET, B.DSCP AS RETURN_TARGET FROM COD_MST AS A " & _
"INNER JOIN (SELECT CODE, DSCP FROM COD_MST WHERE GUBN='QC09' " & _
") AS B ON A.CODE = B.CODE " & _
"WHERE A.GUBN='QC08') CM ON QC.LINE_CD = CM.CODE " & _
"LEFT JOIN (SELECT LINE_CD, SUM(WRK_QTY) AS WRK_QTY FROM WRK_ORD " & _
"WHERE WRK_YMD BETWEEN '$S' AND '$S' GROUP BY LINE_CD " & _
") AS WO ON QC.LINE_CD = WO.LINE_CD " & _
"GROUP BY QC.LINE_CD, QC.LINE_NM, WO.WRK_QTY, PN.GUBUN, CM.LINE_TARGET, CM.RETURN_TARGET " & _
"ORDER BY QC.LINE_CD " _
, Format(DTPDate(0).Value, "YYYYMMDD"), Format(DTPDate(1).Value, "YYYYMMDD"), Format(DTPDate(0).Value, "YYYYMMDD"), Format(DTPDate(1).Value, "YYYYMMDD"))
Rs.Open SqlStmt, CN, adOpenForwardOnly, adLockReadOnly
While Not Rs.EOF
//Start at First Row for First Record from RecordSet (Rs), loop through all Records from RecordSet (Rs)
For RowCount = 1 To LastRow
.Row = RowCount
//Initialize/Re-initialize calculation variables for every Record
LineScrap = 0
CustomerScrap = 0
ResidentScrap = 0
ReworkQTY = 0
FailCost = 0
//Check to see if LastRow (Totals Row)
If .Row = LastRow Then
//If LastRow, populate columns with Total values
For ColCount = 1 To 15
.Col = ColCount
If .Col = 1 Then
ElseIf .Col = 2 Then
.ColMerge = MergeRestricted
ElseIf .Col = 3 Then
.Text = TotalProduction
ElseIf .Col = 4 Then
.Text = Val(Rs.Fields("Line Target"))
ElseIf .Col = 5 Then
.Text = TotalRework
ElseIf .Col = 6 Then
.Text = TotalScrap
ElseIf .Col = 7 Then
.Text = TotalReworkPPM
ElseIf .Col = 8 Then
.Text = TotalScrapPPM
ElseIf .Col = 9 Then
.Text = TotalFailCosts
ElseIf .Col = 10 Then
.Text = Val(Rs.Fields("Return Target"))
ElseIf .Col = 11 Then
.Text = TotalCustReturn
ElseIf .Col = 12 Then
.Text = TotalOnSiteReturn
ElseIf .Col = 13 Then
.Text = TotalCustReturnPPM
ElseIf .Col = 14 Then
.Text = TotalOnSiteReturnPPM
ElseIf .Col = 15 Then
.Text = TotalScrapPPM
Else
End If
Next
//Close database connection
Rs.Close
//Exit Subroutine logic
Exit Sub
End If
//Choose the correct variable to store "Scrap QTY" value from RecordSet (Rs) based on "GUBUN" value of Record
If IsNull(Rs.Fields("Scrap QTY")) = False Then
If Trim(Rs.Fields("GUBUN")) = "Customer" Then
CustomerScrap = Val(Rs.Fields("Scrap QTY"))
ElseIf Trim(Rs.Fields("GUBUN")) = "On Site" Then
ResidentScrap = Val(Rs.Fields("Scrap QTY"))
ElseIf Trim(Rs.Fields("GUBUN")) = "MIP NG" Then
LineScrap = Val(Rs.Fields("Scrap QTY"))
End If
//If "Scrap QTY" is NULL then set correct variable to 0 based on "GUBUN" value of Record
Else
If Trim(Rs.Fields("GUBUN")) = "Customer" Then
CustomerScrap = 0
ElseIf Trim(Rs.Fields("GUBUN")) = "On Site" Then
ResidentScrap = 0
Else
LineScrap = 0
End If
End If
//Store "Rework QTY" in correct variable
//If "Rework QTY" is NULL, store 0
If IsNull(Rs.Fields("Rework QTY")) = False Then
ReworkQTY = Val(Rs.Fields("Rework QTY"))
Else
ReworkQTY = 0
End If
//Populate spread (SSList) with correct values using RecordSet (Rs) and calculated variables
//Line Column
.Col = 1
.Text = Rs.Fields("Line Code")
//Model Column
.Col = 2
.Text = Rs.Fields("Line Name")
//Prod (EA) Column
.Col = 3
//If "Work QTY" Record is Null set cell value to 0
If IsNull(Rs.Fields("Work QTY")) = False Then
.Text = Trim(Val(Rs.Fields("Work QTY")) + LineScrap)
Else
.Text = 0
End If
//Calculate running total for 'Prod (EA)' Column through all Records/loops
TotalProduction = TotalProduction + Val(.Text)
//In Line Target (PPM) Column
.Col = 4
//If "Line Target" Record is Null set cell value to 0
If IsNull(Rs.Fields("Line Target")) = False Then
.Text = Trim(Val(Rs.Fields("Line Target")))
Else
.Text = 0
End If
//In Line Rework QTY Column
.Col = 5
//If "Rework QTY" Record is Null set cell value to 0
If IsNull(Rs.Fields("Rework QTY")) = False Then
.Text = ReworkQTY
Else
.Text = 0
End If
//Calculate running total for 'In Line Rework QTY' Column through all Records/loops
TotalRework = TotalRework + Val(.Text)
//In Line Scrap QTY Column
.Col = 6
//Set cell value to LineScrap variable
.Text = LineScrap
//Calculate running total for 'In Line Scrap QTY' Column through all Records/loops
TotalScrap = TotalScrap + Val(.Text)
//In Line Rework PPM QTY Column
.Col = 7
//If "Work QTY" Record is Null set cell value to 0
If IsNull(Rs.Fields("Work QTY")) = False Then
.Text = Round(ReworkQTY / (Val(Rs.Fields("Work QTY")) + LineScrap) * 10 ^ 6, 6)
Else
.Text = 0
End If
//Calculate running total for 'In Line Rework PPM QTY' Column through all Records/loops
TotalReworkPPM = TotalReworkPPM + Val(.Text)
//In Line Scrap PPM QTY Column
.Col = 8
//If "Work QTY" is Null set cell value to 0
If IsNull(Rs.Fields("Work QTY")) = False Then
.Text = Round(LineScrap / (Val(Rs.Fields("Work QTY")) + LineScrap) * 10 ^ 6, 6)
Else
.Text = 0
End If
//Calculate runing total for 'In Line Scrap PPM QTY' Column through all Records/loops
TotalScrapPPM = TotalScrapPPM + Val(.Text)
//In Line Fail Costs ($) Column
.Col = 9
//If "GUBUN" Record is "MIP NG" and "Cost" Record is Not Null set cell value to "Cost" Record
//Otherwise, set cell value to 0
If Trim(Rs.Fields("GUBUN")) = "MIP NG" Then
If IsNull(Trim(Rs.Fields("Cost"))) = False Then
.Text = Val(Rs.Fields("Cost"))
Else
.Text = 0
End If
Else
.Text = 0
End If
//Calculate running total for 'In Line Fail Costs ($)' Column through all Records/loops
TotalFailCosts = TotalFailCosts + Val(.Text)
//Customer Return Target PPM QTY Column
.Col = 10
//If "Return Target" Record is Null set cell value to 0
If IsNull(Rs.Fields("Return Target")) = False Then
.Text = Trim(Val(Rs.Fields("Return Target")))
Else
.Text = 0
End If
//Customer Return QTY Column
.Col = 11
//Set cell value to CustomerScrap variable
.Text = CustomerScrap
//Calculate running total for 'Customer Return QTY' Column through all Records/loops
TotalCustReturn = TotalCustReturn + Val(.Text)
//On Site Return QTY Column
.Col = 12
//Set cell value to ResidentScrap variable
.Text = ResidentScrap
//Calculate running total for 'On Site Return QTY' Column through all Records/loops
TotalOnSiteReturn = TotalOnSiteReturn + Val(.Text)
//Customer Return PPM QTY Column
.Col = 13
//If "Work QTY" Record is Null set cell value to 0
If IsNull(Rs.Fields("Work QTY")) = False Then
.Text = Round(CustomerScrap / (Val(Rs.Fields("Work QTY")) + LineScrap) * 10 ^ 6, 2)
Else
.Text = 0
End If
//Calculate running total for 'Customer Return PPM QTY' Column through all Records/loops
TotalCustReturnPPM = TotalCustReturnPPM + Val(.Text)
//On Site Return PPM QTY Column
.Col = 14
//If "Work QTY" Record is Null set cell value to 0
If IsNull(Rs.Fields("Work QTY")) = False Then
.Text = Round(ResidentScrap / (Val(Rs.Fields("Work QTY")) + LineScrap) * 10 ^ 6, 2)
Else
.Text = 0
End If
//Calculate running total for 'On Site Return PPM QTY' Column through all Records/loops
TotalOnSiteReturnPPM = TotalOnSiteReturnPPM + Val(.Text)
//Total Loss PPM Column
.Col = 15
//If "Work QTY" Record is Null set cell value to 0
If IsNull(Rs.Fields("Work QTY")) = False Then
.Text = Round((CustomerScrap + LineScrap) / (Val(Rs.Fields("Work QTY")) + LineScrap) * 10 ^ 6, 0)
Else
.Text = 0
End If
//Calculate running total for 'Total Loss PPM' Column through all Records/loops
TotalLossPPM = TotalLossPPM + Val(.Text)
//Move to the next Record in RecordSet (Rs)
Rs.MoveNext
Next
Wend
End With
This code is run with an active connection to a database, CN, with a RecordSet, Rs. The FOR loop basically goes through every column of every row and populates each cell with the correct values needed based on the logic, moving to the next Record in the RecordSet after every row. The last row in my SQL query RecordSet is a totals row that has data only in certain columns. When reaching this last row, it populates the cells with either the calculated running totals or, when available, the values in the RecordSet. After populating the last row of the table, the subroutine ends.
I don't know if anyone has any interest in this problem, but hopefully this can help someone. This may not be the ideal or most efficient way of populating a FarPoint vaSpread component, but it works 100% of the time and depending on your SQL query you can make this future proof. In particular, I have my query set up so all of the joins occur on a single reference table (QC.LINE_MST) populated with line codes or "Line_CD"'s that I would like to see on the table. This enables me to just add new "Line_CD"'s to that reference table so that my query and thus my program will pick it up on the next inquiry. This logic also handles NULL values from the SQL table, setting all NULL values to 0 before any calculations are made or cells are populated. The only time that this logic needs to be updated is when you would like to add new information columns to the table, something that I personally won't need to do.
If anyone has any suggestions for the code, ways to make it more efficient or have cleaner formatting, please leave a comment below.

How can I extract the 'logical_test' from an if statement in excel?

I'm putting together an excel spreadsheet for calculations, and I need to be able to show the formulas to go with the decisions, for the most part its pretty straight forward, but When I come to an 'if' formula in an excel cell, I don't want to show the value_if_true and value_if_false... Just the logical_test value.
Example:
Formula is: =if(and(5<=A1, A1<=10),"Pass", "Fail");
Result will be: "and(5<=A1, A1<=10)"
I need to be able to work with complex logical tests which may include nested if statements, so just splitting at the commas won't work reliably. Similarly the value_if_true and value_if_false statements could also contain if statements.
Any ideas?
If have clear understanding of what you asking for, then you can use something like this (shall be used only with IF() statement :
Function extrIf(ByVal ifstatement As Range) As String
Dim S$, sRev$, x%, k
S = Replace(Replace(ifstatement.Formula, "IF(", "\"), "),", ")|")
sRev = StrReverse(S)
If InStr(1, sRev, "|") > InStr(1, sRev, "\") Or InStr(1, sRev, "|") = 0 Then
x = InStr(1, StrReverse(Left(sRev, InStr(1, sRev, "\"))), ",") - 1
S = Mid(S, 1, Len(S) - InStr(1, sRev, "\") + x) & "|"
End If
sRev = ""
For Each k In Split(S, "|")
If k <> "" Then
If k Like "*\*" Then
sRev = sRev & ", " & Mid(k, InStr(1, k, "\") + 1, 999)
End If
End If
Next
extrIf = Mid(sRev, 3, 999)
End Function
example:
test:
Maybe this is not complete solution for you, but I think it might give you right direction.
If the cell formula starts with an If statement then you can return the logic test (starting after the first open parenthesis) by determining the position of the first comma where the sum of the previous open parenthesis - the sum previous closed = 0.
Formulas
Function ExtractIfTest(Target As Range) As String
Dim ch As String, s As String
Dim openP As Long
Dim x As Long
s = Target.formula
For x = 5 To Len(s)
ch = Mid(s, x, 1)
If Mid(s, x, 1) = "(" Then
openP = openP + 1
ElseIf Mid(s, x, 1) = ")" Then
openP = openP - 1
ElseIf Mid(s, x, 1) = "," And openP = 0 Then
ExtractIfTest = Mid(s, 5, x - 12)
End If
Next
End Function
Results
There might be instances where the is a comma without parenthesis A1,B1. If this happens simple escape them with parenthesis (A1,B1)
I've written an UDF that extract any of the parameters of the target formula. It's close to the one in Thomas answer, but more global and takes into account strings that can enclose commas or parenthesis.
Function ExtractFormulaParameter(Target As Range, Optional Position As Long = 1) As Variant
Dim inString As Boolean
Dim formula As String
Dim st As Long, sp As Long, i As Long, c As String
Dim parenthesis As Long, comma As Long
formula = Target.formula
st = 0: sp = 0
If Position <= 0 Then ExtractFormulaParameter = CVErr(xlErrValue): Exit Function
For i = 1 To Len(formula)
c = Mid$(formula, i, 1)
If inString Then
If c = """" Then
inString = False
End If
Else
Select Case c
Case """"
inString = True
Case "("
parenthesis = parenthesis + 1
If parenthesis = 1 And Position = 1 Then
st = i + 1
End If
Case ")"
parenthesis = parenthesis - 1
If parenthesis = 0 And sp = 0 Then sp = i: Exit For
Case ","
If parenthesis = 1 Then
comma = comma + 1
If Position = 1 And comma = 1 Then sp = i: Exit For
If Position > 1 And comma = Position - 1 Then st = i + 1
If Position > 1 And comma = Position Then sp = i: Exit For
End If
Case Else
End Select
End If
Next i
If st = 0 Or sp = 0 Then
ExtractFormulaParameter = CVErr(xlErrNA)
Else
ExtractFormulaParameter = Mid$(formula, st, sp - st)
End If
End Function
By default it returns the first parameter, but you can also return the second or the third, and it should work with any formula.
Thanks for the replies all. I thought about this more, and ended up coming up with a similar solution to those posted above - essentially string manipulation to extract the text where we expect to find the logical test.
Works well enough, and I'm sure I could use it to extract further logical tests from substrings too.

VB select case not working as expected

I am a total novice with visual basic and teaching myself as I go along. I am building a VB in studio 2008 (I'm obliged to use this version) that logs into a device , transmits log in and password and then transmits commands read from a .txt file using reflections. All of this is working fine. The device executes the command and outputs 1 of 28 possible responses.
I am using select case to evaluate the responses and act accordingly. The device session stops as expected when EXECUTED is seen in the session window, my test data is designed so the first response I get is "EXECUTED", the weird thing is my VB "sees" the EXECUTED message (Case 1) but select case responds as if it has seen FAILED (Case 2), subsequent lines of the test data illicit different cases (5 and 6) but the response is always the next case along. I have tried Case n, case is = n, case "string value" but I get errors.
Here's my code - note that I haven't defined all 28 cases yet but the undefined ones are REM'ed out in my active version. Any ideas or suggestions would be gratefully received!
Option Explicit On
Public Class modCaseSelect
Shared Sub Dev_Responses(ByVal refl)
Dim Result As String
Dim CR = vbCr
Dim Resp As Integer
Dim Dev_Resp(28) As String
Dev_Resp(0) = "RUNNING"
Dev_Resp(1) = "EXECUTED"
Dev_Resp(2) = "FAILED"
Dev_Resp(3) = "SEMANTICS ERROR"
Dev_Resp(4) = "NONEXISTENT"
Dev_Resp(5) = "NOT FOUND"
Dev_Resp(6) = "SPECIAL"
Dev_Resp(7) = "CONFIRM: Y/N"
Dev_Resp(8) = "CONFIRM (Y/N)"
Dev_Resp(9) = "CONFIRM EXECUTION: Y/N"
Dev_Resp(10) = "ALREADY EXECUTED"
Dev_Resp(11) = ""
Dev_Resp(12) = ""
Dev_Resp(13) = ""
Dev_Resp(14) = ""
Dev_Resp(15) = ""
Dev_Resp(16) = ""
Dev_Resp(17) = ""
Dev_Resp(18) = ""
Dev_Resp(19) = ""
Dev_Resp(20) = ""
Dev_Resp(21) = ""
Dev_Resp(23) = ""
Dev_Resp(23) = ""
Dev_Resp(24) = ""
Dev_Resp(25) = ""
Dev_Resp(26) = ""
Dev_Resp(27) = ""
Dev_Resp(28) = "IN PROGRESS"
With refl
Select Case .WaitForStrings(Dev_Resp, "0:4:30") 'checkDev_Resp
Case 0 ' "RUNNING"
Result = Dev_Resp(0)
Resp = MsgBox((Dev_Resp(0) & CR & CR & Continue?"), 17, "Case 0 error")
Case 1 ' "EXECUTED"
Result = Dev_Resp(1)
Resp = MsgBox((Dev_Resp(1) & CR & CR & "Continue?"), 17, "Case 1")
Case 2 ' "FAILED"
Result = Dev_Resp(2)
Resp = MsgBox((Dev_Resp(2) & CR & CR & "Continue?"), 17, "Case 2 error")
Case 3 ' "SEMANTICS ERROR"
Result = Dev_Resp(3)
Resp = MsgBox((Dev_Resp(3) & CR & CR & "Continue?"), 17, "Case 3 error")
Case 4 ' "NONEXISTENT"
Result = Dev_Resp(4)
Resp = MsgBox((Dev_Resp(4) & CR & CR & "Continue?"), 17, "Case 4 error")
Case 5 ' "NOT FOUND"
Result = Dev_Resp(5)
Resp = MsgBox((Dev_Resp(5) & CR & CR & "Continue?"), 17, "Case 5 error")
Case 6 ' "SPECIAL"
Result = Dev_Resp(6)
Resp = MsgBox((Dev_Resp(6) & CR & CR & "Continue?"), 17, "Case 6 error")
Case 7 ' "CONFIRM: Y/N"
Result = Dev_Resp(7)
.Transmit("Y" & CR)
Case 8 ' "CONFIRM (Y/N)"
Result = Dev_Resp(8)
.Transmit("Y" & CR)
Case 9 ' "CONFIRM EXECUTION: Y/N"
Result = Dev_Resp(9)
.Transmit("Y" & CR)
Case 10 ' "ALREADY EXECUTED"
Result = Dev_Resp(10)
Resp = MsgBox((Dev_Resp(10) & CR & CR & "Continue?"), 17, "Case 10 error")
Case 11 ' ""
Result = Dev_Resp(11)
Case 12 ' ""
Result = Dev_Resp(12)
Case 13 ' ""
Result = Dev_Resp(13)
Case 14 ' ""
Result = Dev_Resp(14)
Case 15 ' ""
Result = Dev_Resp(15)
Case 16 ' ""
Result = Dev_Resp(16)
Case 17 ' ""
Result = Dev_Resp(17)
Case 18 ' ""
Result = Dev_Resp(18)
Case 19 ' ""
Result = Dev_Resp(19)
Case 20 ' ""
Result = Dev_Resp(20)
Case 21 ' ""
Result = Dev_Resp(21)
Case 22 ' ""
Result = Dev_Resp(22)
Case 23 ' ""
Result = Dev_Resp(23)
Case 24 ' ""
Result = Dev_Resp(24)
Case 25 ' ""
Result = Dev_Resp(25)
Case 26 ' ""
Result = Dev_Resp(26)
Case 27 ' ""
Result = Dev_Resp(27)
Case 28 ' "IN PROGRESS"
Result = Dev_Resp(28)
Resp = MsgBox((Dev_Resp(28) & CR & CR & "Continue?"), 17, "Case 28 error")
Case Else
End Select
End With
End Sub
End Class
You are missing a double quote " in your first Case. Try changing it to this:
Case 0 ' "RUNNING"
Result = Dev_Resp(0)
Resp = MsgBox((Dev_Resp(0) & CR & CR & "Continue?"), 17, "Case 0 error")
Notice I've added the double quote before "Continue?".
Get rid of the With statement. Create and assign a holder variable and use that with the select statement. Doing so will allow you to see what is actually getting passed into the select statement by setting a stop point in the debugger.
Dim temp_resp as integer = refl.WaitForStrings(Dev_Resp, "0:4:30")
Select Case temp_resp
'the case statements here.
End Select
Reflections WaitForStrings uses a zero-based array parameter, but it returns a 1-based index of strings. Waitforstrings sees array entry zero as the first valid entry so the first select case (Case = 1) corresponds to array entry 0.

VBA and Cases, assigning value based off of value in a range

I have grades for students and levels of proficiency.
for example, for 5th grade, there are four levels of proficiency. The four levels of proficiency correspond to letters of the alphabet, so if a 5th grader got a letter assignment of B, then his proficiency level would be "Below Proficient" since for any letter in A to R would get a level of "Below Proficient" I was wondering how to do this with cases,
My rough idea of code was the following:
Function ConvertScores(Grade, Letter_Score)
Select Case ConvertScore(5, like "[A-R]")
Case ConvertScore(5, like"
ConvertScores = "F & P Remedial"
Case 2
ConvertScores = "F & P Below Proficient"
Case 3
ConvertScores = "F & P Proficient"
Case 4
ConvertScores = "F & P Advanced"
End Function
So yea, I wish VBA had a list object, what is the list object in VBA?
EDIT: I was able to do it with multiple If statements, but it seems to me that cases would have been a better way to go.
Here is my code that I want to use with cases instead of multiple if-thens
Function ConvertScoresMOY(Grade, Letter_Score) As String
If Grade = "5" And Letter_Score Like "[A-R]" Then
ConvertScoresMOY = "F & P Remedial"
ElseIf Grade = "5" And Letter_Score Like "[S-T]" Then
ConvertScoresMOY = "F & P Below Proficient"
ElseIf Grade = "5" And Letter_Score Like "[U-V]" Then
ConvertScoresMOY = "F & P Proficient"
ElseIf Grade = "5" And Letter_Score Like "[W-Z]" Then
ConvertScoresMOY = "F & P Advanced"
Else:
End If
End Function
Lets just start from insert all the conditions in one simple condition:
If Grade = "5" Then
End If
then you can use SWITCH CASE:
Select Case Letter_Score
Case Letter_Score Like "[A-R]"
ConvertScoresMOY = "F & P Remedial"
Case val Letter_Score Like "[S-T]"
ConvertScoresMOY = "F & P Below Proficient"
End Select
You could create your dictionary in another sheet, then use the VLOOKUP() function to search for a value on the first column and get the value on the second.