Null Check a column value that can be Null without two DLookups? - vba

How do I remove one DLookup from code similar to this example?
Dim sCustomerName as String
If IsNull(DLookup("sName", "tblCustomers", "iCustomerID=12345")) Then
MsgBox "Customer name is null!"
Else
sCustomerName = DLookup("sName", "tblCustomers", "iCustomerID=12345")
End If
In VBA, reading a column value that can be Null (or missing) into a variable, and then comparing it to Null gives an error. Is there a better way to do these checks?
Obviously, if there's no performance penalty, it's not bad code because of the performance hit. Is there?
Even if not, it has duplicated/copy-pasted code. How can it be rewritten to avoid this?

A Variant data type can store Null values. Try to get the value and check if Null.
Dim customerName As Variant
customerName = DLookup("sName", "tblCustomers", "iCustomerID=12345")
If Not IsNull(customerName) Then
'do stuff
End If
Another approach is the Nz() function:
You can use the Nz function to return zero, a zero-length string (" "), or another specified value when a Variant is Null.
Dim customerName As String
customerName = Nz(DLookup("sName", "tblCustomers", "iCustomerID=12345"), vbNullString)
If customerName <> vbNullString Then
'do stuff
End If

Related

Calling an Access VBA function with IF statement as an argument

Assuming I have the following MS Access 2016 function:
Public Function TestFunction(ByVal strTest As String) As String
Is there a way to call the function with something similar to:
strReturn = TestFunction(If a = 1 Then "Apple" Else "Orange")
I would like to write something as compact as possible, and avoid having to write multiple lines for the call, such as:
If a = 1 Then strArg = "Apple" Else strArg = Orange
strReturn = TestFunction(strArg)
You can accomplish this using IIf, e.g.:
strReturn = TestFunction(IIf(a=1,"Apple","Orange"))
But this is generally discouraged when working with expressions other than constants, because IIf will always evaluate both the then and else argument before returning the appropriate value depending on the outcome of the test expression, which can sometimes lead to undesired results.
For example, evaluating the following expression at the Immediate Window (Ctrl+G) will result in a division by zero error, even though the else expression will never be returned:
?iif(true,"Apple",1/0)
If the value for a comes from a well defined range then the solution would be to use either the Choose Function for a contiguous range or Switch Function for a non contiguous well defined range.
Of course the more grown up solution would be to replace both Choose or Switch with a Dictionary.
Why not something like this for multiple possibilities
Dim myArg as String
Select Case a
Case 1: myArg = "Apple"
Case 2: myArg = "Orange"
Case 3: myArg = "Pear"
'etc.
End Select
strReturn = TestFunction(myArg)

Why is my DLookup producing "invalid use of null" Error: 94

In my Access VBA code, I have several DLOOKUP functions in a sequence. They are all the same except for the value the parameter they are returning from 'studys'.
The last one is occasionally producing the error:
invalid use of null
The line of code producing the error is:
necropsy = DLookup("[Necropsy]", "Studys", "[Primary Key] = " & ID)
The necropsy value in Study's is a null Date occasionally but that is why I am looking it up.
In comparison the line above it:
studyEnd = DLookup("[Study End]", "Studys", "[Primary Key] = " & ID)
This code runs fine. It would never return a null value as study end is never null.
What's up?
If you have Dim necropsy As Date, necropsy can not accept Null because Null is not a Date/Time value.
If you want to allow necropsy to accept Null, make it a Variant instead: Dim necropsy As Variant
A date cannot be Null in VBA.
If you simply remove the variable declaration it will be returned as a date when not null.

String.IsNullOrEmpty VS IsDBNull in validation when using IIF and IF ELSE

This is not clear to me, so if anyone can explain in detailed if what is the difference of the two functions (IsDBNull and String.IsNullOrEmpty) of VB.Net.
Below is my scenario why i ask that, i have column Company in my table which the value is NULL, then i used IIF function of vb.net to validate if it is NULL then assign empty string (""), else assign the value coming from data table
Scenario:
Below is using String.IsNullOrEmpty and i get a conversion error:
Conversion from type 'DBNULL' to 'String' is not valid.
txtCompany.Text = IIf(String.IsNullOrEmpty(dtSample.Rows(grdInfo.SelectedIndex).Item("Company")).ToString, "", dtSample.Rows(grdInfo.SelectedIndex).Item("Company"))
However when i replace String.IsNullOrEmpty by IsDBNull , the validation works fine.
txtCompany.Text = IIf(IsDBNull(dtSample.Rows(grdInfo.SelectedIndex).Item("Company")).ToString, "", dtSample.Rows(grdInfo.SelectedIndex).Item("Company"))
EDIT:
And it is confusing because if i did the validation using IF ELSE condition (see sample code below) with the use of String.IsNullOrEmpty, it works fine.
If String.IsNullOrEmpty(dtSample.Rows(grdInfo.SelectedIndex).Item("Company").ToString) = True Then
txtCompany.Text = ""
Else
txtCompany.Text = dtSample.Rows(grdInfo.SelectedIndex).Item("Company").ToString
End If
The confusing part is when i used IIF(String.IsNullOrEmpty...etc) it returns an error. but when i used the normal IF String.IsNullOrEmpty(dtSample.Rows....etc) = True it works fine.
Any explanation would much appreciated. Thanks
TL;DR
String.IsNullOrEmpty() checks only for Empty ([blank] i.e. '' or "") or Null values it does not check DBNull, if any field coming from database has its value DBNull it will raise an error.
IsDBNull() checks for DBNull (it is not same as Null)
.ToString will convert DBNull to Empty String i.e. ''
Details
Consider following SQL table example (using SQL Server as base)
Table Structure:
Column_Name Type and Size Other Properties
---------------- ------------- ----------------------
Company_ID int IDENTITY(1,1) NOT NULL
Company_Name nvarchar (50) NOT NULL
Company_Address nvarchar (50) NULL
INSERT statements:
INSERT [tbl_company] ([Company_Name], [Company_Address]) VALUES ('ABC', 'QWERT')
INSERT [tbl_company] ([Company_Name], [Company_Address]) VALUES ('ASD', ' ')
INSERT [tbl_company] ([Company_Name], [Company_Address]) VALUES ('XYZ', '')
INSERT [tbl_company] ([Company_Name]) VALUES ('PQR')
Table Data:
Company_ID Company_Name Company_Address
----------- ---------------- ---------------
1 ABC QWERT
2 ASD [SPACE]
3 XYZ [BLANK]
4 PQR NULL
Testing Company_Address with IsNullOrEmpty() and IsDBNull() using a SqlDataReader (r):
Company_ID IsNullOrEmpty(r("Company_Address")) IsDBNull(r("Company_Address"))
---------- ----------------------------------- ------------------------------
1 False False
2 False False
3 True False
4 ERROR True
And now specific to the Question
What the OP is trying here, lets consider all statements one by one
The IIF statement with IsNullOrEmpty (wrong)
txtCompany.Text = IIf(String.IsNullOrEmpty(dtSample.Rows(grdInfo.SelectedIndex).Item("Company")).ToString, "", dtSample.Rows(grdInfo.SelectedIndex).Item("Company"))
In this statement OP is accessing the value as dtSample.Rows(grdInfo.SelectedIndex).Item("Company") and checking it with IsNullOrEmpty() and then converting the result of IsNullOrEmpty to string using .ToString i.e. IsNullOrEmpty(value).ToString(). If the value is DBNull it will always return an error. The correct way to use it is
IsNullOrEmpty(dtSample.Rows(grdInfo.SelectedIndex).Item("Company").ToString)
See last part Company")).ToString vs Company").ToString), just a case of MISPLACED ")"
Second (IIF with IsDBNull) and third (IF with IsNullOrEmpty) statements of OP are correct
txtCompany.Text = IIf(IsDBNull(dtSample.Rows(grdInfo.SelectedIndex).Item("Company")).ToString, "", dtSample.Rows(grdInfo.SelectedIndex).Item("Company"))
If String.IsNullOrEmpty(dtSample.Rows(grdInfo.SelectedIndex).Item("Company").ToString) = True Then
txtCompany.Text = ""
Else
txtCompany.Text = dtSample.Rows(grdInfo.SelectedIndex).Item("Company").ToString
End If
As regards to the second statement OP is correctly ordering the parameters i.e. first Company field is converted to string using dtSample.Rows(grdInfo.SelectedIndex).Item("Company").ToString. This converts any DBNull to Empty string and then it checked for IsNullOrEmpty. Now as the value has already been converted to EmptyString it will not give any error.
Old discussions with OP
The difference is clear in your text. Your first statement is:
txtCompany.Text = IIf(String.IsNullOrEmpty(dtSample.Rows(grdInfo.SelectedIndex).Item("Company")).ToString, "", dtSpecifierRebate.Rows(grdInfo.SelectedIndex).Item("Company"))
and the second one is
If String.IsNullOrEmpty(dtSample.Rows(grdInfo.SelectedIndex).Item("Company").ToString) = True Then
Now break them apart, first statement (IIF)
String.IsNullOrEmpty(dtSample.Rows(grdInfo.SelectedIndex).Item("Company")).ToString
'Item("Company")).ToString
And second part (IF)
String.IsNullOrEmpty(dtSample.Rows(grdInfo.SelectedIndex).Item("Company").ToString)
'Item("Company").ToString)
Found any difference?
In first statement you are converting the result of IsNullOrEmpty to String
In second one you are converting .Item("Company") ToString and then comparing it.
If .Item("Company") returns DBNull
then IsNullOrEmpty failed because .Item("Company") returned type DBNull whereas IsNullOrEmpty checks for null
IsDBNull worked because it checks for DBNull
All point of a misplaced bracket ")" It's a typo
And regarding your usage of these statements:
If and IIF need to check the results as booleans and not as strings
It is better recommended to remove the ToString portion of your statements
You cannot mix and match String.IsNullOrEmpty and IsDBNull because they work on two different things. The first on strings, the second on Data items read from the database.
But a very important element of this is that your code is invalid. Neither "Scenario" snippet compiles under Option Strict. If you leave VB to guess what you mean, you will get confusing results.
Snippet 1:
txtCompany.Text = IIf(String.IsNullOrEmpty(dtSample.Rows(grdInfo.SelectedIndex).Item("Company")).ToString, "", dtSample.Rows(grdInfo.SelectedIndex).Item("Company"))
Simplified:
Dim foo = IIf(String.IsNullOrEmpty(zDT.Rows(23).Item("Name")).ToString,
"", zDT.Rows(23).Item("Name"))
This is illegal because zDT.Rows(23).Item("Name") is an object, but String.IsNullOrEmpty expects a string. Your ToString is misplaced - it is not converting the db Item, it is converting the entire IIF bool expresion!
The compiler warns you of both withOption Strict On.
The conversion throws an exception because VB must convert the db Object item ( zDT.Rows(23).Item("Name")) to a string. The way it does it results in an error when when the db data is DBNull.
Snippet 2:
txtCompany.Text = IIf(IsDBNull(dtSample.Rows(grdInfo.SelectedIndex).Item("Company")).ToString, "", dtSample.Rows(grdInfo.SelectedIndex).Item("Company"))
Simplified:
foo = IIf(IsDBNull(zDT.Rows(23).Item("Name")).ToString,
"", zDT.Rows(23).Item("Name"))
This is slightly better but a string is till being used in place of the Boolean expression. When fixed, you have:
IsDBNull(zDT.Rows(23).Item("Name"))
IsDBNull is testing a database item (Object) to see if it has data. It will work. IsNullOrEmpty should not be used to test for DBNull and cant with Option Strict. You'd have to convert the dbItem to string first, then it will only work depending on how you convert.
' cant use string method to test an object
String.IsNullOrEmpty(zDT.Rows(23).Item("Name"))
' this will work:
String.IsNullOrEmpty(zDT.Rows(23).Item("Name").ToString)
' this will not:
String.IsNullOrEmpty(CStr(zDT.Rows(23).Item("Name")))
Use DBNull tests for data objects, and IsNullOrEmpty on strings.
Also, if you use the newer If operator in place of the old IIf function you can avoid other issues. The operator allows short circuiting, the syntax is the same:
Dim foo = If(bool expr, True result, False result)
IsDBNull Function :
Returns a Boolean value that indicates whether an expression evaluates to the System.DBNull class.
IsDBNull returns True if the data type of Expression evaluates to the DBNull type; otherwise, IsDBNull returns False.
The System.DBNull value indicates that the Object represents missing or nonexistent data. DBNull is not the same as Nothing, which indicates that a variable has not yet been initialized. DBNull is also not the same as a zero-length string (""), which is sometimes referred to as a null string.
example :
Dim testVar As Object
Dim nullCheck As Boolean
nullCheck = IsDBNull(testVar)
testVar = ""
nullCheck = IsDBNull(testVar)
testVar = System.DBNull.Value
nullCheck = IsDBNull(testVar)
' The first two calls to IsDBNull return False; the third returns True..
String.IsNullOrEmpty :
Indicates whether the specified string is null or an Empty string.
IsNullOrEmpty is a convenience method that enables you to simultaneously test whether a String is null or its value is Empty.
Example :
Class Sample
Public Shared Sub Main()
Dim s1 As String = "abcd"
Dim s2 As String = ""
Dim s3 As String = Nothing
Console.WriteLine("String s1 {0}.", Test(s1))
Console.WriteLine("String s2 {0}.", Test(s2))
Console.WriteLine("String s3 {0}.", Test(s3))
End Sub
Public Shared Function Test(s As String) As String
If String.IsNullOrEmpty(s) Then
Return "is null or empty"
Else
Return String.Format("(""{0}"") is neither null nor empty", s)
End If
End Function
End Class
'The example displays the following output:
'String s1 ("abcd") is neither null nor empty.
'String s2 is null or empty.
'String s3 is null or empty.
The IsNullOrEmpty function checks whether or not a string is empty or null. DBNull is not null (Nothing), but rather is a class that indicates a value from a database does not exist. IsDbNull checks for whether or not a value equals DBNull.
You may not be getting the error you mention in the question on the line of code referenced as this runs just fine for me:
strTest = IIf(String.IsNullOrEmpty(DBNull.Value.ToString), "", DBNull.Value)

insert null into date column in database

I've got a gridview in vb.net and am trying to insert a null value into a database column - unfortunately, i keep seeing 1900-01-01 being inserted. below is my code and i need help.
Protected Sub gvPOItems_RowCommand(sender As Object, e As System.Web.UI.WebControls.GridViewCommandEventArgs) Handles gvPOItems.RowCommand
If (e.CommandName = "save") Then
For i As Integer = 0 To gvPOItems.Rows.Count - 1
Dim row As GridViewRow = gvPOItems.Rows(i)
Dim value3 As String = DirectCast(row.Cells(9).FindControl("txtbxExpireDate"), TextBox).Text.Replace("'", "''")
If String.IsNullOrEmpty(value3) Or value3.ToString() Is Nothing Then
value3 = DBNull.Value.ToString()
End If
Next
End Sub
It works when the value isn't null, because you're injecting the value in that string:
INSERT INTO table (datecolumn) VALUES ('2014-08-04')
However, DBNull.Value.ToString() resolves to an empty string. When you then try and insert said empty string into your database via a SQL-injection-prone approach, you're really running a query something like:
INSERT INTO table (datecolumn) VALUES ('')
And that puts in the default value of 1900-01-01. What you need is:
INSERT INTO table (datecolumn) VALUES (NULL)
You need to parameterize your queries. Then you can pass Nothing directly to your SQL command, and it will work. Aside from that, you have to shift some abstraction so that you only add ' characters if the argument is not Nothing, and if it is, then you need to pass the string NULL.
As for the code you do have, there are some bugs here, too.
Dim value3 As String = DirectCast(row.Cells(9).FindControl("txtbxExpireDate"), TextBox).Text.Replace("'", "''")
If String.IsNullOrEmpty(value3) Or value3.ToString() Is Nothing Then
value3 = DBNull.Value.ToString()
End If
value3 is a string, and as per documentation, string.ToString() returns an unconverted instance of the original value. In other words, String.IsNullOrEmpty(value3) already does what you're checking for in your latter condition, except if the latter condition were ever true it would throw a NullReferenceException (or whatever the equivalent of that is in VB, I don't know whether it uses Nothing because I've literally never written anything in VB before).
Next, you should be aware that value3 will never be Nothing. It comes directly from a TextBox.Text property, and will consequently be at most an empty string. That's fine, because you check for that in IsNullOrEmpty, but since you added on the Is Nothing I thought I'd better point that out.
So basically, long story short, parameterize your queries. For all our sakes. But if you absolutely aren't going to, this should fix this particular problem:
Dim value3 As String = DirectCast(row.Cells(9).FindControl("txtbxExpireDate"), TextBox).Text
If String.IsNullOrEmpty(value3)
value3 = "NULL"
Else
value3 = "'" + value3.Replace("'", "''") + "'"
End If
You'll then have to adjust your later code to not add in those quotes, itself.

MS Query with various multiple value parameters where there can be an empty parameter

I have 5 multiple select listboxes in Excel. The selected content of every listbox is each written in one cell, separated with a comma.
For example in cell A1 all the selected names: Tim, Miranda, Laura
Those cells are the criteria for an Access query (where clause).
I open MS Query and add the following where clause to the query and define the parameters:
Where (Instr(?, [name])>0 And Instr(?, [number])>0 And Instr(?, [city])>0 And Instr(?, [phone])>0 And Instr(?, [email])>0)
It works quite well, however if one of the parameter fields is empty (for example the user didn't select any city) the query returns all lines, where city is empty instead of ignoring that clause in this case.
How do I solve this? Perhaps there is another solution using VBA and dynamic SQL.
Note: I have to use Excel for the listboxes instead of Access formulas because the tool shall also be used by persons who do not have access to the database.
I would use the LIKE operator.
In Excel populate other cells and point the parameters to them
=if(<cityCell> ="","_%",<cityCell>&"%")
and change the query to
Where [name] like ? And [number] like ? And[CITY] like and [phone] like ? And [email]like ?
In this way you can handle other transgressions by users i.e. change the Excel formula to
=if(<cityCell> ="","_%",PROPER(<cityCell>)&"%") OR
So user entry of toronto will look for Toronto. I use this a lot with UPPER as lots of databases contain uppercase entries.. i.e. Canadian postal codes
From my experience, it may be returning null in some cases and affecting your comparison in the clause.
what happens when you try the len function:
Where Instr(?, [name])>0 And Instr(?, [number])>0 And (Instr(?, [number])>0 and
NOT ISNULL([CITY])) And Instr(?, [phone])>0 And Instr(?, [email])>0)**
See the code change above. Sorry, saw your update, I just need to get a grip on the problem. Do you want all records including those that have "empty" cities? But I thought you wanted it to skip those? Another way I search for this is - NOT ISNULL([CITY]) . I work in an environment with a LOT of Access databases and they are quirky!
I solved the Problem by using dynamic SQL in VBA. If nothing is selected, I link all rows in the listbox to a string. This is an example for one listbox which contains names (the code does not contain the necessary connection to the database):
Dim string1 As String
Dim rngParams As Range, rngCell As Range
Dim i As Long
'String name
With Worksheets("Table1")
For i = 0 to .ListBox1.ListCount - 1
If .ListBox1.Selected(i) Then
If string1 = "" Then string1 = "("
string1 = string1 & "'" & .ListBox1.List(i) & "',"
End If
Next i
End With
If string1 <> "" Then
string1 = Left(string1, Len(string1) - 1)
string1 = string1 & ")"
End If
'If nothing is selected, use all names and write them into one string
If string1 = "" Then
string1 = "("
With Worksheets("table2")
Set rngParams = .Range("allNames")
For Each rngCell In rngParams.Cells
string1 = string1 & "'" & rngCell.Value & "',"
Next rngCell
string1 = Left(string1, Len(string1) - 1)
string1 = string1 & ")"
End With
End If
strSQL = "SELECT Name FROM accesstable WHERE Name IN " & string1