I have an If statement which I was assuming was comparing each value to each other. However it seems no matter what the values are (e.g. all values contain a count of 4) it goes to the else. Am I missing something in the If statement?
If rst![CountOfProvider] = rst![CountOfDelivery Type] = rst![CountOfBU Creator] = rst![CountOfOrigin] = rst![CountOfSub-Destination] = rst![CountOfDestination Zipcode] = rst![CountOfCity] = rst![CountOfState] = rst![CountOfCost Zone] = rst![CountOfRate] = rst![CountOfMarket Name] Then
chk = False
Else
chk = True
End If
VBA doesn't perform that sequence of comparisons as you seem to expect.
Consider this simpler example from the Immediate window ...
Debug.Print 2 = 2
True
Debug.Print 2 = 2 = 2
False
I'm uncertain how VBA handles those multiple equality comparisons, but suspect it may be testing the first and then comparing the result from that with the next ... sort of like this ...
Debug.Print (2 = 2) = 2
False
The first comparison returns True, which is the integer -1 ...
Debug.Print CInt(2 = 2)
-1
So that means the final comparison would be equivalent to this ...
Debug.Print -1 = 2
And naturally that returns False.
The computationally quickest way is to hard code the comparisons. The more
extensible way is to test via a loop.
HansUp makes a good comment - you should be wary of potential null values and add in a handler to deal with them as desired (e.g. using Nz() in Access or IsNull() in any host environment)
'Method 1
If rst![CountOfProvider] = rst![CountOfDelivery Type] And _
rst![CountOfProvider] = rst![CountOfBU Creator] And _
...etc...Then
chk = False
Else
chk = True
End If
'Method 2
chk = True
For Each ele In Array(rst![CountOfDelivery Type], rst![CountOfBU Creator],...your others...)
If ele <> rst![CountOfProvider] Then
chk = False
Exit For
End If
Next ele
Related
I have an access database that tracks our internal quality assurance checks for staff. They have 8 items we track them on and each of those items has 4 possible values:
Pass (1)
Non-Pass (2)
FYI (3)
NP&FYI (4).
You can see these 4 set on the left in the column topped by "CPT". [1]. What I would like to have happen is if ANY of those 8 fields are FYI (ID 3) it sets the FYI checkbox to true. If ANY are Non-Pass (ID 2) It sets the passing checkboxes to false. If ANY of the fields are NP&FYI (ID 4) I want the two checkboxes to be set to non-pass and FYI. The code I am trying is:
Private Sub Form_AfterUpdate()
If (Me.Diagnosis And Me.CPT And Me.DOS And Me.UpDownCode And
Me.ChargeCorrections And Me.ChurnEsc And Me.Protocol And Me.BillServProv)
= 1 Then
Me.FYI = 0
Me.QAFail = -1
End If
If (Me.Diagnosis Or Me.CPT Or Me.DOS Or Me.UpDownCode Or
Me.ChargeCorrections Or Me.ChurnEsc Or Me.Protocol Or Me.BillServProv) = 2
Then
Me.QAFail = 0
End If
If (Me.Diagnosis Or Me.CPT Or Me.DOS Or Me.UpDownCode Or
Me.ChargeCorrections Or Me.ChurnEsc Or Me.Protocol Or Me.BillServProv) = 3
Then
Me.FYI = -1
End If
If (Me.Diagnosis Or Me.CPT Or Me.DOS Or Me.UpDownCode Or
Me.ChargeCorrections Or Me.ChurnEsc Or Me.Protocol Or Me.BillServProv) = 4
Then
Me.FYI = -1
Me.QAFail = 0
End If
End Sub
Unfortunately I'm getting inconsistent results. If I set field like CPT to non-pass then the FYI checkbox tics and the QAFail box does not clear. If I set other fields to other values I get all kinds of different results. I'm assuming its because there is confusion between the possible values of the fields being interpreted by the successive If statements. I thought about using CASE, but I couldn't figure out how to set the case value based on 8 different fields. I'm a novice at VBA, can anyone point me in the right direction?
June7's alt code (works)
Dim strCodes As String
With Me
strCodes = Nz(.Diagnosis & .CPT & .DOS & .UpDownCode & _
.ChargeCorrections & .ChurnEsc & .Protocol & .BillServProv, "")
End With
If InStr(strCodes, 2) > 0 Then
Me.QAFail = 0
ElseIf InStr(strCodes, 3) > 0 Then
Me.txtFYI = -1
ElseIf InStr(strCodes, 4) > 0 Then
Me.txtFYI = -1
Me.QAFail = 0
ElseIf strCodes <> "" Then
Me.txtFYI = 0
Me.QAFail = -1
End If
End Sub
The If condition returns either a True or False (-1 or 0) not the 1, 2, 3, 4 you are testing for. Every If block executes so the last one that meets criteria returns result. Use ElseIf then only one End If. Use a With Me block and don't have to repeat the Me qualifier. Why does posted code not show line continuation character? Code assumes every field has a value. If any field is Null or empty string, the test for ID 1 will always return False even if other 7 fields have a 1.
With Me
If (.Diagnosis = 1 And .CPT = 1 And .DOS = 1 And .UpDownCode = 1 And _
.ChargeCorrections = 1 And .ChurnEsc = 1 And .Protocol = 1 And .BillServProv = 1) Then
...
ElseIf (...) Then
...
ElseIf (...) Then
...
ElseIf (...) Then
...
End If
End With
Use that same syntax for the ElseIf expressions using the Or operator.
Make sure testing values in order of priority. If ID 3 should have precedence over 2 and 4 then test for 3 first. I would think 2 has precedence but your narrative contradicts.
Consider alternative code:
Dim strCodes As String
With Me
strCodes = Nz(.Diagnosis & .CPT & .DOS & .UpDownCode & _
.ChargeCorrections & .ChurnEsc & .Protocol & .BillServProv, "")
End With
If InStr(strCodes, 2) > 0 Then
...
ElseIf InStr(strCodes, 3) > 0 Then
...
ElseIf InStr(strCodes, 4) > 0 Then
...
ElseIf strCodes <> "" Then
...
End If
This is the first time I've asked a question although I have found the solutiion to many of my broblems here over the years.
I have a frustrating problem to which I cannot find an answer. I want to do the initial read prior to a read sequentially through a linked table opened as a dynaset DAO recordset using a specific index name as defined on the table.
My code returns error 3251 'operation is not supported...' on the .index line. No doubt there is an obvious solution (Mind you, I'm trying to avoid opening a SQL query which would be the obvious answer).
Public Function IOrdCustomerStock(CustomerID As Long, ProductID As Long, KeepRsOpen As Boolean, Optional UseIndexName As String) As Boolean
Set zcls_CS.CS_rs = CurrentDb.OpenRecordset(Name:=CS_TableName, Type:=RecordsetTypeEnum.dbOpenDynaset)
With zcls_CS.CS_rs
If Not IsMissing(UseIndexName) Then
.Index = UseIndexName
End If
.FindFirst "CS_CustomerID = " & CustomerID & " and CS_ProductID = " & ProductID
If .NoMatch Then
zcls_CS.CS_EOF = True
Else
zcls_CS.CS_EOF = False
zcls_CS.CS_ID = .Fields("[ID]")
zcls_CS.CS_CustomerID = .Fields("[CS_CustomerID]")
zcls_CS.CS_PhysSalesStock = .Fields("[CS_PhysSalesStock]")
zcls_CS.CS_ProductID = .Fields("[CS_ProductID]")
zcls_CS.CS_PurQuantityRecvd = .Fields("[CS_PurQuantityRecvd]")
zcls_CS.CS_PurUnitDesc = .Fields("[CS_PurUnitDesc]")
zcls_CS.CS_PurUnitFactor = .Fields("[CS_PurUnitFactor]")
zcls_CS.CS_SaleQuantityAlloc = .Fields("[CS_SaleQuantityAlloc]")
zcls_CS.CS_SaleQuantityOrdered = .Fields("[CS_SaleQuantityOrdered]")
zcls_CS.CS_SaleUnitDesc = .Fields("[CS_SaleUnitDesc]")
zcls_CS.CS_SaleUnitFactor = .Fields("[CS_SaleUnitFactor]")
End If
End With
If Not KeepRsOpen Then
Call IOclCustomerStock
End If
IOrdCustomerStock = Not zcls_CS.CS_EOF
End Function
Once I'd restricted the problem to linked tables, I found the following post:
https://social.msdn.microsoft.com/Forums/office/en-US/d402a8d2-0771-458c-b57e-09e2d6f0c536/trying-to-open-a-linked-table-whats-going-on?forum=accessdev
I don't pretend to understand the OpenDatabase parameters but it works. I just need to add the usual error handling to my little proof of concept:
Public Function IOksInitRsIX1CustomerStock(UseIndexName As String, CustomerID As Long, ProductID As Long) As Boolean
Set zcls_CS.CS_rs = OpenDatabase(Mid(DBEngine(0)(0).TableDefs(CS_TableName).Connect, 11)).OpenRecordset(CS_TableName)
With zcls_CS.CS_rs
zcls_CS.CS_rs.Index = UseIndexName
If (CustomerID > 0 And ProductID > 0) Then
.Seek "=", CustomerID, ProductID
Else
If CustomerID > 0 Then
.Seek "=", CustomerID
End If
End If
If .NoMatch Then
zcls_CS.CS_EOF = True
Else
zcls_CS.CS_EOF = False
zcls_CS.CS_ID = .Fields("[ID]")
zcls_CS.CS_CustomerID = .Fields("[CS_CustomerID]")
zcls_CS.CS_PhysSalesStock = .Fields("[CS_PhysSalesStock]")
zcls_CS.CS_ProductID = .Fields("[CS_ProductID]")
zcls_CS.CS_PurQuantityRecvd = .Fields("[CS_PurQuantityRecvd]")
zcls_CS.CS_PurUnitDesc = .Fields("[CS_PurUnitDesc]")
zcls_CS.CS_PurUnitFactor = .Fields("[CS_PurUnitFactor]")
zcls_CS.CS_SaleQuantityAlloc = .Fields("[CS_SaleQuantityAlloc]")
zcls_CS.CS_SaleQuantityOrdered = .Fields("[CS_SaleQuantityOrdered]")
zcls_CS.CS_SaleUnitDesc = .Fields("[CS_SaleUnitDesc]")
zcls_CS.CS_SaleUnitFactor = .Fields("[CS_SaleUnitFactor]")
End If
End With
IOksInitRsIX1CustomerStock = Not zcls_CS.CS_EOF
End Function
j and d both evalulate the same function however when I use If IsNull to catch any Null Values the value of d is not correctly evaluated. What is causing this?
Dim d As Integer
Dim j As Integer
j = DSum("Count", "qry_nihr_unique")
If IsNull(d = DSum("Count", "qry_nihr_unique")) Then
MsgBox "No records were found for the data criteria you entered"
GoTo ESub
Else
Me.un_p.Value = d
End If
Debug.Print "j = " & j
Debug.Print "d = " & d
j = 58
d = 0
Updated Code After Answer
Dim d
d = DSum("Count", "qry_nihr_unique")
If IsNull(d) Then
MsgBox "No records were found for the data criteria you entered"
GoTo ESub
Else
Me.un_p.Value = d
End If
After HansUp's answer below I believe this is the most efficient way to write this.
Within IsNull(), the code checks whether d is equal to the DSum() expression. It's an equality test, and nothing is assigned to d. So the value of d remains unchanged --- it was initialized as zero and remains zero.
The situation is much like this Immediate window session:
? DSum("id", "tblFoo")
134
d = 0
? (d = DSum("id", "tblFoo"))
False
? d
0
? IsNull(d = DSum("id", "tblFoo"))
False
? d
0
The following statement will assign the DSum() result to d; not test whether the two are equal:
d = DSum("id", "tblFoo")
? d
134
Is it possible to edit data that is grabbed from a recordset? In my case, I am trying to add quantities together so that I can get a total. So an example of what I am trying to do would be:
<%
set rs = server.CreateObject("ADODB.recordset")
totalqty = 0
do NOT while rs.EOF
totalqty = totalqty + rs("QTY")
loop
>%
Whenever I tried to do something like this, I would always get an 'Type MisMatch' Error and I'm not sure how to resolve this problem.
As always, any and all help would be appreciated.
Try to "cast" the value in the recordset like so:
CDbl( rs.fields("QTY").value )
This will cast the value to a double. If the value is null you will get en error so you have to check that first...
Or you can write a function to always get the correct type:
public function parse(value, alternative)
dim val
val = trim(value & "")
parse = alternative
if val = "" then exit function
on error resume next
select case varType(parse)
case 2, 3 'integer, long
parse = cLng(val)
case 4, 5 'single, double
parse = cdbl(val)
case 6 'currency
parse = ccur(val)
case 7 'date
parse = cDate(val)
case 11 'bool
parse = cBool(val)
case 8 'string
parse = value & ""
case else
on error goto 0
lib.throwError("type not supported. val:" & value & " alt:" & alternative)
end select
on error goto 0
end function
dim val : val = rs("QTY")
val = parse(val, 0)
' now val is always an integer (either the value from db or 0)
ulluoink's solution will work, but this is simpler...
function ToDbl(vIn, nDefault)
'Convert a variant to an integer using default where necessary
if isnull(vIn) then
ToDbl = nDefault
else
if IsNumeric(CStr(vIn)) Then
ToDbl = CDbl(vIn)
else
ToDbl = nDefault
end if
end if
end function
Then just call:
totalqty = totalqty + ToDbl(rs("QTY"), 0)
I am trying to compare two recordsets. the first rs1 has random records. The second rs2 has the standard values for those records. Initially I am looking to take each of the records and see if they match with the standard set of values in the second recordset. There are four fields in each record set to be compared and all four must match.
I just need some help in the loop. I am trying to write the non matching records to an excel file.
Here is what I have so far
While Not rs1.EOF
With rs1
.MoveFirst
With rs2
.MoveFirst
While Not rs2.EOF
counter = counter + 1
a = 0
If rs1!Kk = rs2!Kk Then a = a + 1
If rs1!CC = rs2!CC Then a = a + 1
If rs1!HN = rs2!HN Then a = a + 1
If rs3!TN = rs2!TN Then a = a + 1
If a > 3 Then GoTo correct
.MoveNext
If rs2.EOF Then
If rs!Table_Name <> "table1" Then
i = i + 1
j = 1
counter = counter + 1
objSht.Cells(i, j).Value = "casenum" & rs1.Fields(1)
j = j + 1
stat_counter = stat_counter + 1
End If
If i = 65500 Then
Set wbexcel = objexcel.ActiveWorkbook
''//Set objSht = wbexcel.Worksheets("Sheet2")
Set objSht = wbexcel.Worksheets.Add
i = 2
End If
End If
correct:
rs1.MoveNext
Wend
End With
End With
Also any ideas on how i can segregate based on 2 of fields matching with standard and 3 of the fields matching with the standard values
Are the recordsets already sorted? I'm guessing that's the case since you move to the next rs2 on a non match. Personally i'd specify a sort to make 100% sure.
Also I'd test this pretty thoroughly with a small test dataset with a few edge cases to make sure you get what you expect.
With the above in mind your code looks like it'd work but i have a few small recommendations to make it easier to read.
First i'd recommend ditching the nested With rs1 and With rs2. Just refer to each recordset explicitly so you can clearly see what is happening to each rs. eg:
If a > 3 Then GoTo correct
.MoveNext
becomes
If a > 3 Then GoTo correct
rs2.MoveNext
Next your if statements with a = a + 1 could do with some tidying. eg:
If rs1!Kk = rs2!Kk and rs1!CC = rs2!CC and rs1!HN = rs2!HN and rs3!TN = rs2!TN then
''// Do Nothing or maybe increase a count or whatever :)
else
WriteToExcel(objSht , rs1.fields)
end if
You'll need to write a function called WriteToExcel() but this will make the next step easier. I think you want to write to different sheets depending on the matches?
If rs1!Kk = rs2!Kk and rs1!CC = rs2!CC and rs1!HN = rs2!HN and rs3!TN = rs2!TN then
''// Do Nothing
else if rs1!Kk = rs2!Kk and rs1!CC = rs2!CC and rs1!HN = rs2!HN then
WriteToExcel(objSht2 , rs1.fields)
else
WriteToExcel(objSht , rs1.fields)
end if
You may also want to look at switches in the case where you need any two matches, rather than specific matches as above... oh and variable j seems a bit superfluous.
My gut says you are doing something sub-optimally; however, in the if statement If rs2.EOF Then, why not add a comparison to a and then redirect to a different Excel file for 0, 1, 2 and 3
Bruit Force and Ignorance, but definitely segregated.