Har*_*nch 4 excel vba excel-vba access-vba
我试图了解Null是什么以及Empty变量是什么.它们是一样的吗?空字符串如何适应?
创建MS Access表时,为什么字段有"允许零长度字符串"选项?
我一直在努力将Access数据库中的数据加载到变量中(例如使用DAO或ADO),我发现自己必须声明我使用Variant的所有变量.这对我来说似乎是错的.
有没有人有任何好的示例代码来演示这些差异如何,你能解释为什么我可能会使用变体.
任何人都可以建议.
(我已经发布了自己的答案,其中包含许多简单的示例代码,这些代码对我有所帮助.它显示了很多函数如何与变体一起工作,我希望有类似困难的人发现这很有用)
变量是唯一可以存储空值或Null值的变量类型,因此声明如下:
Dim aVar as Variant
Dim aVar2 ' if no type is given then it's declared as a variant
Run Code Online (Sandbox Code Playgroud)
声明后,变体立即存储没有值,它是空的.您也可以使用空变量aVar = Empty为空,并且它将再次为空.
当变量存储为空时,这些都是正确的:
aVar = Empty
IsEmpty(aVar)
Run Code Online (Sandbox Code Playgroud)
您还可以将Variant变量的值设置为Null
aVar = Null
Run Code Online (Sandbox Code Playgroud)
现在这些都是假的
aVar = Empty
IsEmpty(aVar)
Run Code Online (Sandbox Code Playgroud)
但是, IsNull(aVar)这是真的.
当您使用VBA变量来存储来自数据库表的数据时,Null特别有用,该数据库表允许将NULL值存储在其字段中.在这种情况下,通常建议所有变量都需要适应存储NULL.所以它们都需要变体,因为这是存储NULL的唯一数据类型.
这非常不幸,因为使用更强类型的变量会更好.
存储Null的变量与变量为Empty的变量不同.Null表示已将值分配给变量,值为Null.
这会让人感到困惑,因为数据库使用Null来指示字段中没有存储任何值,并且大多数数据库允许任何数据类型的字段为Null.当数据库字段存储Null时,它类似于VBA变量变量,它刚刚被声明为值的"容器"而尚未给出值.变量,就像表字段一样,是一个没有任何东西的容器.
但是,如果从数据库表中为VBA变量变量赋予Null值,则它会存储它为Null的事实,因为它与从未给定值的情况不同,并且是您的程序可能希望以不同方式处理的信息.
另请注意,存储空字符串""的变量与空字符不同.
即""不等于Empty(它也与Null不同!)
当使用MS Access表来存储文本时,我建议不要将"允许零长度"字段属性设置为true,这是默认设置,因为这意味着您的数据库字段将能够存储""(即空字符串)以及Null值.您编写的任何代码都必须使用该字段存储""或Null的可能性.使用Null更容易.
(很少需要在数据库表中存储空字符串).
另一个有用的技术是用于MyString = Nz(MyStringDatabaseField)将任何空值转换为空字符串.至少那时你的代码只需测试空字符串而不是Nulls.此技术还将简化使用存储空字符串的访问表的代码.有时使用`MyInteger = Nz(MyIntegerDatabaseField)将任何空值转换为0可能是合适的,但是我对此非常不舒服,因为0比空字符串更有意义并且真正为空<> 0!
请注意,在其表之间使用OUTER JOIN的SQL语句可能导致返回的记录集在字段中包含NULL值,其中定义基础表字段以防止存储NULL.
请注意,如果不使用变量数据类型,则可能会意外使用默认值.例如
Dim aInt As Integer
aInt = Empty
Debug.Print aInt, " This will print 0, as 0 is the default value for integer variables"
Run Code Online (Sandbox Code Playgroud)
下面的代码帮助我理解了可用于检查变量变量的各种函数之间的区别
Sub ExperimentsWithVariants()
Dim aInt As Integer
aInt = Empty
Debug.Print aInt, " This will print 0, as 0 is the default value for integer variables"
Dim avar As Variant ' The results shown as comments below were created when aVar was declared as a variant
Debug.Print "-----------------------"
Debug.Print "NOT SET:"
Debug.Print "-----------------------"
Debug.Print "TypeName(avar)", TypeName(avar) ' Empty
Debug.Print "aVar = Empty ", (avar = Empty) ' True
Debug.Print "aVar", avar ' '' ie blank
Debug.Print "IsNull(aVar)", (IsNull(avar)) ' False
Debug.Print "IsEmpty(aVar)", (IsEmpty(avar)) ' True
Debug.Print "aVar = """"", (avar = "") ' True
Debug.Print "aVar = 0", (avar = 0) ' True
If avar = Empty Then
Debug.Print " "
Debug.Print "avar = Empty so the above would be the same if you set avar = Empty explicitly"
Debug.Print " """
Else
avar = Empty
Debug.Print " "
Debug.Print "-----------------------"
Debug.Print " SET TO Empty"
Debug.Print "-----------------------"
Debug.Print "TypeName(avar)", TypeName(avar) ' Empty
Debug.Print "aVar = Empty ", (avar = Empty) ' True
Debug.Print "aVar", avar ' '' ie blank
Debug.Print "IsNull(aVar)", (IsNull(avar)) ' False
Debug.Print "IsEmpty(aVar)", (IsEmpty(avar)) ' True
Debug.Print "aVar = """"", (avar = "") ' True
Debug.Print "aVar = 0", (avar = 0) ' True
End If
avar = Null
Debug.Print " "
Debug.Print "-----------------------"
Debug.Print " SET TO NULL"
Debug.Print "-----------------------"
Debug.Print "TypeName(avar)", TypeName(avar) ' Null
Debug.Print "aVar = Empty ", (avar = Empty) ' Null
Debug.Print "aVar", avar ' Null
Debug.Print "IsNull(aVar)", (IsNull(avar)) ' True
Debug.Print "IsEmpty(aVar)", (IsEmpty(avar)) ' False
Debug.Print "aVar = """"", (avar = "") ' Null
Debug.Print "aVar = 0", (avar = 0) ' Null
avar = ""
Debug.Print " "
Debug.Print "-----------------------"
Debug.Print " SET TO EMPTY STRING ie """""
Debug.Print "-----------------------"
Debug.Print "TypeName(avar)", TypeName(avar) '
Debug.Print "aVar = Empty ", (avar = Empty) ' True
Debug.Print "aVar", avar ' '' ie blank
Debug.Print "IsNull(aVar)", (IsNull(avar)) ' False
Debug.Print "IsEmpty(aVar)", (IsEmpty(avar)) ' False
Debug.Print "aVar = """"", (avar = "") ' True
Debug.Print "aVar = 0", (avar = 0) ' String
' Note
' Is empty returns false, whereas ="" returns NULL
avar = 1.23
Debug.Print "-----------------------"
Debug.Print "SET to 1.23:"
Debug.Print "-----------------------"
Debug.Print "TypeName(avar)", TypeName(avar) ' Double
Debug.Print "aVar = Empty ", (avar = Empty) ' True
Debug.Print "aVar", avar ' '' ie blank
Debug.Print "IsNull(aVar)", (IsNull(avar)) ' False
Debug.Print "IsEmpty(aVar)", (IsEmpty(avar)) ' True
Debug.Print "aVar = """"", (avar = "") ' True
Debug.Print "aVar = 0", (avar = 0) ' True
' You can test for both an IsEmpty AND an empty string (ie "" ) AND a null value with:
' IIf(Len(avar & vbNullString)
Debug.Print "-----------------------"
Debug.Print "Using IIf(Len(avar & vbNullString) "
Debug.Print "-----------------------"
avar = ""
Debug.Print """""=", IIf(Len(avar & vbNullString) = 0, "Null, IsEmpty, or Empty String", "NOT")
avar = "1"
Debug.Print "1 = ", IIf(Len(avar & vbNullString) = 0, "Null IsEmpty,or Empty String", "NOT")
avar = Null
Debug.Print "Null = ", IIf(Len(avar & vbNullString) = 0, "Null, IsEmpty or Empty String", "NOT")
avar = Empty
Debug.Print "Empty = ", IIf(Len(avar & vbNullString) = 0, "Null or Empty String", "NOT")
Debug.Print "-----------------------"
Debug.Print "using TypeName"
Debug.Print "-----------------------"
Dim dbl1 As Double
Debug.Print "TypeName(dbl1) ", TypeName(dbl1) ' Double
Dim int1 As Integer
Debug.Print "TypeName(int1) ", TypeName(int1) ' Integer
Dim str1 As String
Debug.Print "TypeName(str1) ", TypeName(str1) ' String
End Sub
Sub ExperimentsWithNz()
Debug.Print " "
Debug.Print "---------------------------------------------------------------------- "
Debug.Print "---------------------------------------------------------------------- "
Debug.Print "1a Nz(Null)="""" =", Nz(Null) = ""
Debug.Print "1b IsNull(Nz(Null)) =", IsNull(Nz(Null)) ' False
Debug.Print "---------------------------------------------------------------------- "
Dim aVar As Variant
Debug.Print "2a Nz(aVar) Unassigned =", Nz(aVar) ' Null
aVar = Empty
Debug.Print "2b Nz(aVar) Empty =", Nz(aVar) ' Null
aVar = Null
Debug.Print "2c Nz(aVar) Null =", Nz(aVar) ' Null
Debug.Print "2d IsNull(Nz(aVar)) Null=", (IsNull(Nz(aVar))) ' Null
aVar = ""
Debug.Print "2e Nz(aVar) """" =", Nz(aVar) ' ' ie an empty string
Debug.Print "---------------------------------------------------------------------- "
Dim str1 As String
Debug.Print "3a Nz(str1) Unassigned =", Nz(str1) ' 0
str1 = Empty
Debug.Print "3b Nz(str1) Empty =", Nz(str1) ' 0
Debug.Print "---------------------------------------------------------------------- "
Dim int1 As Integer
Debug.Print "4a Nz(int1) Unassigned =", Nz(int1) ' 0
int1 = Empty
Debug.Print "5b Nz(int1) Empty =", Nz(int1) ' 0
' The following line cannot run as a string cannot be assigned Null
' str1 = Null
End Sub
Sub DealingWithEmptyStringsInADatabaseTable()
Dim aVar As Variant
Debug.Print "UNdeclared: ", Nz(aVar, 1)
aVar = Empty
Debug.Print "aVar=Empty ", Nz(aVar, 1)
aVar = Null
Debug.Print "aVar=Null ", Nz(aVar, 1)
aVar = ""
Debug.Print "aVar="""" ", Nz(aVar, 1)
Debug.Print " -------------------------------------------------------"
Debug.Print "Dealing with empty string in a database table"
aVar = ""
Debug.Print "IIf(aVar = "", 1, 0) ", IIf(aVar = "", 1, 0)
Debug.Print " "
Debug.Print " "
Debug.Print "-------------------------------------------------------"
Debug.Print "Dealing with a table field that can have Null or an Empty string"
Debug.Print "leads to more complex code than if is just stores NULL."
Debug.Print " "
Debug.Print "The code below shows WHY you should set the ""; Allow Zero Length "" property of access tables to false"
Debug.Print " "
aVar = Null
Debug.Print "1 Null : IIf(Nz(aVar & """" ,"""") = """", 1, 0) ", IIf(aVar & "" = "", 1, 0)
aVar = ""
Debug.Print "2 Empty String: IIf(Nz(aVar & """" ,"""") = """", 1, 0) ", IIf(aVar & "" = "", 1, 0)
Debug.Print " "
Debug.Print "Both lines 1 and 2 above work."
Debug.Print " "
Debug.Print " "
aVar = Null
Debug.Print "3 Null : Nz(aVar, 1) ", Nz(aVar, 1)
aVar = ""
Debug.Print "4 Empty String: Nz(aVar, 1) ", Nz(aVar, 1)
Debug.Print " "
Debug.Print "however, line 4 does not work for empty string."
Debug.Print "3 & 4 are much simpler than 1 & 2, but if your field can store """" and Null"
Debug.Print "you have to use 1 & 2. Which is a shame as 3 & 4 are simpler."
Debug.Print "Queries and code accessing this data can get messy"
End Sub
Run Code Online (Sandbox Code Playgroud)
添加到 HarveyFrench 的答案中......
VBA 的变体可以优雅地处理 NULL,也可以处理 Empty。的概念Empty具有值得争议的优点,并且在许多数据库系统中都没有体现。
NULL 表示未获得或无法获得的值。这是一个对于正确处理 OUTER JOIN 至关重要的概念。OUTER JOIN 从一个表(或子查询)开始,该表将选择其所有行。然后使用 JOIN 的条件链接到另一个表(或子查询)。在 INNER JOIN 的情况下,如果表(或子查询)之间的条件无法满足(即解析为 TRUE),则不会选择这种情况的行。然而,在 OUTER JOIN 的情况下,不满足的 JOIN 条件仍将导致第一个表(或子查询)中的行被选择,并为另一个表(或子查询)的每一列选择一组 NULL。这些 NULL 将表明 JOIN 失败;这些将是无法获得的值。
“空”与 NULL 不同。NULL表示未获取。“空”意味着“该字段故意留空”。这是一个微妙的区别,但“空”是确定的东西,而 NULL 是未知的东西。故意的空白是故意的,而不是错误,明确表示首先没有什么可找到的。
有些人可能故意使用 NULL 来指示列的“空”;或者他们可能使用 NULL 来表示除无法获得之外的其他内容。有些人会说以这种方式使用 NULL 是不好的做法。这样做的原因是,现在没有明确的方法来区分有意留空和未知,并且您可能会在复杂的查询中感到困惑。我想说的是“小心一点,你确定 NULL 是使这一切正常工作的最佳或唯一方法吗?”
至于空字符串,"",这在很大程度上是一个值。就像某些明确已知的事情一样。它也是一种特定的数据类型 - 即字符串的数据类型(或文本或 varchar 或任何字符数组)。您可以将其称为“空”,尽管实际上它仅限于需要字符串的位置。实际上,这可能等同于同一件事。但它并不是真正的“空”,而且它肯定不是 NULL。
就我个人而言,我认为 NULL 更多的是一个信号,就像您在浮点数中可能遇到的“非数字”条件一样。换句话说,NULL 不是一个特定值。这是没有价值的信号。当您查找三值逻辑 (3VL) 时,尤其是涉及 SQL 和 NULL 的地方,您将更好地理解为什么条件NULL=NULL根本不为 TRUE。
“空”也是一个信号,但是是“无论什么都等于空白”的信号。因此,条件 EMPTY="" 和 EMPTY=0 都将为 TRUE - 即使""=0可能无法计算。