IsNull、IsEmpty、=Empty 和空字符串(即“”)有啥不同,为啥我可以使用变体
Posted
技术标签:
【中文标题】IsNull、IsEmpty、=Empty 和空字符串(即“”)有啥不同,为啥我可以使用变体【英文标题】:What is the different between IsNull, IsEmpty, =Empty, and an empty string ie "" and why might I use variantsIsNull、IsEmpty、=Empty 和空字符串(即“”)有什么不同,为什么我可以使用变体 【发布时间】:2015-09-09 21:56:44 【问题描述】:我试图了解 Null 是什么以及 Empty 变量是什么。他们是一样的吗?空字符串如何适应?
在创建 MS Access 表时,为什么字段有“允许零长度字符串”选项?
我一直在努力将 Access 数据库中的数据加载到变量中(例如,使用 DAO 或 ADO),我发现自己必须声明我使用 Variant 的所有变量。这对我来说似乎很错误。
有没有人有任何很好的示例代码来演示它们之间的区别,你能解释一下为什么我可能会使用一个变体。
谁能给点建议。
(我已经发布了自己的答案,其中包含许多对我有帮助的简单示例代码。它展示了许多函数如何与变体一起使用,我希望有类似困难的人觉得这很有用)
【问题讨论】:
这个 question 和 Code Review 上的答案有些相关。 我发现这很难读,而且有点超出我的理解。但是,我从中得到了一些东西,我现在不能说什么。当我知道更多时,我会重新阅读它。谢谢 【参考方案1】:变体是唯一可以存储空值或 Null 值的变量类型,因此声明如下:
Dim aVar as Variant
Dim aVar2 ' if no type is given then it's declared as a variant
变量声明后立即不存储任何值并且它是空的。
您也可以使用aVar = Empty
将空分配给变体,它将再次为空。
当变量存储为空时,这些都是正确的:
aVar = Empty
IsEmpty(aVar)
您还可以将 Variant 变量的值设置为 Null
aVar = Null
现在这些都是假的
aVar = Empty
IsEmpty(aVar)
但是,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)
将任何空值转换为空字符串。至少你的代码只需要测试空字符串而不是空值。这种技术还将简化使用存储空字符串的访问表的代码。有时使用 `MyInteger=Nz(MyIntegerDatabaseField) 将任何空值转换为 0 可能是合适的,但我对此感到非常不舒服,因为 0 比空字符串更有意义,而且真的是 Null 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"
下面的代码帮助我理解了可用于检查变体变量的各种函数之间的区别
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
【讨论】:
还有""
不是 vbNullString
... 但这是一个不同的主题 =)
天啊,我的父亲,虽然是这样!请展开。
试试Debug.Print StrPtr(""), StrPtr(vbNullString)
——两者中只有一个有实际的内存地址! ;-)
这超出了我的范围。原样:Debug.Print IsNull(Null), IsNull(vbNull)
vbNull 是一个 VBA 常量(值为 1)。对我来说,从记录集(通常是字符串)导入时转换 NULL 的可靠方法是检查每个潜在的 dbVal 并将其分配给我的 VBA 字符串:myStr = IIf(IsNull(dbVal), vbNullString, dbVal)
【参考方案2】:
添加到 HarveyFrench 的答案...
VBA 的 Variant 可以优雅地处理 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 是最好的或唯一的方法吗?”
至于空字符串""
,这是一个非常有价值的值。就像在某些确定的事情中一样。它也是一种特定的数据类型 - 即字符串的数据类型(或 text 或 varchar 或任何可能被视为字符数组的数据类型)。您希望将其称为“空”,尽管实际上它仅限于预期字符串的位置。在实践中,它可能等同于同一件事。但它并不是真正的“空”,而且肯定不是 NULL。
我个人认为 NULL 更像是一个信号,就像您在浮点数中可能遇到的“非数字”条件。换句话说,NULL 不是一个特定的值。这是没有价值的信号。当您查找三值逻辑 (3VL) 时,特别是在涉及 SQL 和 NULL 的情况下,您会更好地理解为什么条件 NULL=NULL
根本不是 TRUE。
“空”也是一个信号,但是一个“任何等于空白”的信号。因此,条件 EMPTY="" 和 EMPTY=0 都将为 TRUE - 即使 ""=0
可能不会计算。
【讨论】:
关于查询中 OUTER JOIN 的 OUTER 端的所有字段的优点可能会返回为 NULL,因此需要将变量声明为变体。关于空字符串。如果有人在填写表格并想要 0 个鸡蛋,他们输入 0。如果有人留下“我想要鸡蛋控件为空”,则存储 Null。但是,如果他们填写中间名,它就会被存储。如果他们不想填写,则将其留空。如果他们没有中间名,他们也会将其留空。 因此,他们可能需要在表单上勾选“我没有中间名复选框”以指示 Null 的含义,这会导致在中间名中存储一个空字符串,表明他们没有没有。他们得到蹩脚的代码来处理它。我更喜欢将复选框链接到另一个名为“我没有中间名”的字段。我讨厌在数据库中存储空字符串。总是麻烦。你不能“看到”他们在那里。 今晚的最后一件事,关于“允许零长度字符串”:bytes.com/topic/access/answers/892927-allow-zero-length-yes-no以上是关于IsNull、IsEmpty、=Empty 和空字符串(即“”)有啥不同,为啥我可以使用变体的主要内容,如果未能解决你的问题,请参考以下文章