博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

处理空值

Posted on 2007-07-11 19:03  懒人ABC  阅读(726)  评论(0)    收藏  举报
处理空值

在列中的值未知或缺失时,在关系数据库中使用空值。空既不是空字符串(对于 character 或 datetime 数据类型),也不是零值(对于 numeric 数据类型)。ANSI SQL-92 规范规定,空必须对于所有数据类型均相同,以便以一致的方式处理所有空。System.Data.SqlTypes 命名空间通过实现 INullable 接口,提供空语义。System.Data.SqlTypes 中的每个数据类型都具有它自己的 IsNull 属性以及可被分配给该数据类型的实例的 Null 值。

空和三值逻辑

在列定义中允许空值将三值逻辑引入您的应用程序。可以将比较计算为以下三个条件之一:

  • True

  • False

  • Unknown

因为空被视作未知,所以,对两个空值进行彼此比较,其结果不被视为相等。在使用算术运算符的表达式中,如果任何操作数为空,结果也为空。

空和 SqlBoolean

任意 System.Data.SqlTypes 之间的比较都将返回 SqlBoolean。每个 SqlType 的 IsNull 函数都返回 SqlBoolean 并可用于检查是否有空值。下面的真值表显示在存在空值时 AND、OR 和 NOT 这三个运算符的计算方式。(T=true,F=false,U=unknown 或空。)

真值表

理解 ANSI_NULLS 选项

System.Data.SqlTypes 提供与在 SQL Server 中设置 ANSI_NULLS 选项时相同的语义。如果上述任何操作数或参数为空,则所有算术运算符(+、-、*、/、%)、位运算符(~、&、|)和大多数函数都返回空,只有属性 IsNull 除外。

ANSI SQL-92 标准不支持 WHERE 子句中的 columnName = NULL。在 SQL Server 中,ANSI_NULLS 选项既控制数据库中的默认可空性,也控制对空值的比较计算。如果启用 ANSI_NULLS(这是默认设置),则在测试空值时在表达式中必须使用 IS NULL 运算符。例如,在 ANSI_NULLS 为 on 时,以下比较始终生成 unknown:

colname > NULL

与包含空值的变量的比较也生成 unknown:

colname > @MyVariable

使用 IS NULL 或 IS NOT NULL 谓词来测试是否有空值。这可能会增加 WHERE 子句的复杂性。例如,AdventureWorks Customer 表中的 TerritoryID 列允许空值。如果 SELECT 语句用于测试是否有空值以及测试其他内容,则它必须包含 IS NULL 谓词:

SELECT CustomerID, AccountNumber, TerritoryID
FROM AdventureWorks.Sales.Customer
WHERE TerritoryID IN (1, 2, 3)
OR TerritoryID IS NULL

如果在 SQL Server 中将 ANSI_NULLS 设置为 off,则可以创建使用等于运算符来比较空值的表达式。但是,您无法阻止不同的连接为该连接设置空选项。不管连接的 ANSI_NULLS 设置如何,使用 IS NULL 测试是否有空值始终有效。

DataSet 中不支持将 ANSI_NULLS 设置为 off,这将始终遵循用于处理 System.Data.SqlTypes 中的空值的 ANSI SQL-92 标准。

DataSet 和空值

空值是特殊的值类型,并且其存储和赋值语义在不同类型系统和存储系统中是不同的。Dataset 在设计上可用于不同类型和存储系统。

本节描述用于在不同类型系统中将空值赋给 DataRow 中的 DataColumn 的空语义。

DBNull.Value

此赋值对于任何类型的 DataColumn 均有效。如果该类型实现 INullable,则 DBNull.Value 被强制为相应的强类型空值。

SqlType.Null

所有 System.Data.SqlTypes 数据类型均实现 INullable。如果可以使用隐式强制转换运算符将强类型的空值转换为该列的数据类型,则应该进行赋值。否则,将引发无效强制转换异常。

null

如果对于给定的 DataColumn 数据类型“null”是合法值,则它被强制为与 INullable 类型 (SqlType.Null) 关联的适当 DbNull.Value 或 Null。

derivedUdt.Null

对于 UDT 列,始终基于与 DataColumn 关联的类型存储空值。考虑以和 DataColumn 关联的 UDT 为例,它并不实现 INullable,但其子类实现 INullable。在此例子中,如果赋予了与派生类关联的强类型空值,则它被存储为非类型化的 DbNull.Value,因为空存储始终与 DataColumn 的数据类型一致。

Note注意

当前在 DataSet 中不支持 Nullable<T> 或 Nullable 结构。

多列(行)赋值

DataTable.Add、DataTable.LoadDataRow 或接受映射到某一行的 ItemArray 的其他 API 将“null”映射到 DataColumn 的默认值。如果数组中的某个对象包含 DbNull.Value 或其强类型的副本,则应用上述相同规则。

此外,以下规则适用于 DataRow.["columnName"] 空赋值的实例:

  1. 默认的 default 值是 DbNull.Value,它适用于除强类型空列之外的所有值。在强类型空列中,默认值是相应的强类型空值。

  2. 在序列化为 XML 文件(如在“xsi:nil”中)期间,永远不写出空值。

  3. 在序列化为 XML 时始终写出所有非空值,包括默认值。这与 XSD/XML 语义不同。在 XSD/XML 语义中,空值 (xsi:nil) 是显式的并且默认值是隐式的(如果在 XML 中不提供,则验证分析程序可以从关联的 XSD 架构获取它)。对于 DataTable 则情况恰好相反:空值是隐式的,默认值是显式的。

  4. 对于从 XML 输入读取的行的所有缺少的列值,都赋予 NULL。使用 NewRow 或类似方法创建的行被赋予 DataColumn 的默认值。

  5. IsNull 方法为 DbNull.Value 和 INullable.Null 都返回 true。

SqlTypes 和 DataSet

任何 System.Data.SqlTypes 实例的默认值都是空。

System.Data.SqlTypes 中的空是特定于类型的,并且无法用单个值(例如 DbNull)表示。使用 IsNull 属性可以检查是否有空值。

赋予空值

空值可被赋给 DataColumn,如以下代码示例所示。您可以将空值直接赋给 SqlTypes 变量,而不会引发异常。

示例

以下代码示例创建一个 DataTable,它具有两列,分别定义为 SqlInt32SqlString。该代码添加一行已知值和一行空值,然后循环访问 DataTable,将这些值赋给变量并在控制台窗口中显示输出。

Visual Basic
Private Sub WorkWithSqlNulls()
Dim table As New DataTable()
' Specify the SqlType for each column.
Dim idColumn As DataColumn = _
table.Columns.Add("ID", GetType(SqlInt32))
Dim descColumn As DataColumn = _
table.Columns.Add("Description", GetType(SqlString))
' Add some data.
Dim row As DataRow = table.NewRow()
row("ID") = 123
row("Description") = "Side Mirror"
table.Rows.Add(row)
' Add null values.
row = table.NewRow()
row("ID") = SqlInt32.Null
row("Description") = SqlString.Null
table.Rows.Add(row)
' Initialize variables to use when
' extracting the data.
Dim isColumnNull As SqlBoolean = False
Dim idValue As SqlInt32 = SqlInt32.Zero
Dim descriptionValue As SqlString = SqlString.Null
' Iterate through the DataTable and display the values.
For Each row In table.Rows
' Assign values to variables. Note that you 
' do not have to test for null values.
idValue = CType(row("ID"), SqlInt32)
descriptionValue = CType(row("Description"), SqlString)
' Test for null value with ID column
isColumnNull = idValue.IsNull
' Display variable values in console window.
Console.Write("isColumnNull={0}, ID={1}, Description={2}", _
isColumnNull, idValue, descriptionValue)
Console.WriteLine()
Next row
End Sub
static private void WorkWithSqlNulls()
{
DataTable table = new DataTable();
// Specify the SqlType for each column.
DataColumn idColumn =
table.Columns.Add("ID", typeof(SqlInt32));
DataColumn descColumn =
table.Columns.Add("Description", typeof(SqlString));
// Add some data.
DataRow nRow = table.NewRow();
nRow["ID"] = 123;
nRow["Description"] = "Side Mirror";
table.Rows.Add(nRow);
// Add null values.
nRow = table.NewRow();
nRow["ID"] = SqlInt32.Null;
nRow["Description"] = SqlString.Null;
table.Rows.Add(nRow);
// Initialize variables to use when
// extracting the data.
SqlBoolean isColumnNull = false;
SqlInt32 idValue = SqlInt32.Zero;
SqlString descriptionValue = SqlString.Null;
// Iterate through the DataTable and display the values.
foreach (DataRow row in table.Rows)
{
// Assign values to variables. Note that you 
// do not have to test for null values.
idValue = (SqlInt32)row["ID"];
descriptionValue = (SqlString)row["Description"];
// Test for null value in ID column.
isColumnNull = idValue.IsNull;
// Display variable values in console window.
Console.Write("isColumnNull={0}, ID={1}, Description={2}",
isColumnNull, idValue, descriptionValue);
Console.WriteLine();
}

此示例显示以下结果:

isColumnNull=False, ID=123, Description=Side Mirror
isColumnNull=True, ID=Null, Description=Null

将空值与 SqlTypes 和 CLR 类型进行比较

在比较空值时,理解 Equals 方法在 System.Data.SqlTypes 中对空值的计算方式以及与它使用 CLR 类型的方式之间的差别十分重要。所有 System.Data.SqlTypes Equals 方法都使用数据库语义对空值进行计算:如果其中任何一个值为空或两个值都为空,则比较结果将为空。在另一方面,如果两个 System.Data.SqlTypes 都为空,则对它们使用 CLR Equals 方法的结果将为 true。这反映了使用实例方法(例如 CLR String.Equals 方法)和使用静态/共享方法 SqlString.Equals 之间的差别。

以下示例对此加以说明,它首先给 SqlString.Equals 方法和 String.Equals 方法分别传递了一对空值,然后又传递了一对空字符串,之后演示结果中的差别。

Visual Basic
Private Sub CompareNulls()
' Create two new null strings.
Dim a As New SqlString
Dim b As New SqlString
' Compare nulls using static/shared SqlString.Equals.
Console.WriteLine("SqlString.Equals shared/static method:")
Console.WriteLine("  Two nulls={0}", SqlStringEquals(a, b))
' Compare nulls using instance method String.Equals.
Console.WriteLine()
Console.WriteLine("String.Equals instance method:")
Console.WriteLine("  Two nulls={0}", StringEquals(a, b))
' Make them empty strings.
a = ""
b = ""
' When comparing two empty strings (""), both the shared/static and
' the instance Equals methods evaluate to true.
Console.WriteLine()
Console.WriteLine("SqlString.Equals shared/static method:")
Console.WriteLine("  Two empty strings={0}", SqlStringEquals(a, b))
Console.WriteLine()
Console.WriteLine("String.Equals instance method:")
Console.WriteLine("  Two empty strings={0}", StringEquals(a, b))
End Sub
Private Function SqlStringEquals(ByVal string1 As SqlString, _
ByVal string2 As SqlString) As String
' SqlString.Equals uses database semantics for evaluating nulls.
Dim returnValue As String = SqlString.Equals(string1, string2).ToString()
Return returnValue
End Function
Private Function StringEquals(ByVal string1 As SqlString, _
ByVal string2 As SqlString) As String
' String.Equals uses CLR type semantics for evaluating nulls.
Dim returnValue As String = string1.Equals(string2).ToString()
Return returnValue
End Function
    private static void CompareNulls()
{
// Create two new null strings.
SqlString a = new SqlString();
SqlString b = new SqlString();
// Compare nulls using static/shared SqlString.Equals.
Console.WriteLine("SqlString.Equals shared/static method:");
Console.WriteLine("  Two nulls={0}", SqlStringEquals(a, b));
// Compare nulls using instance method String.Equals.
Console.WriteLine();
Console.WriteLine("String.Equals instance method:");
Console.WriteLine("  Two nulls={0}", StringEquals(a, b));
// Make them empty strings.
a = "";
b = "";
// When comparing two empty strings (""), both the shared/static and
// the instance Equals methods evaluate to true.
Console.WriteLine();
Console.WriteLine("SqlString.Equals shared/static method:");
Console.WriteLine("  Two empty strings={0}", SqlStringEquals(a, b));
Console.WriteLine();
Console.WriteLine("String.Equals instance method:");
Console.WriteLine("  Two empty strings={0}", StringEquals(a, b));
}
private static string SqlStringEquals(SqlString string1, SqlString string2)
{
// SqlString.Equals uses database semantics for evaluating nulls.
string returnValue = SqlString.Equals(string1, string2).ToString();
return returnValue;
}
private static string StringEquals(SqlString string1, SqlString string2)
{
// String.Equals uses CLR type semantics for evaluating nulls.
string returnValue = string1.Equals(string2).ToString();
return returnValue;
}
}

此代码生成以下输出内容:

SqlString.Equals shared/static method:
Two nulls=Null
String.Equals instance method:
Two nulls=True
SqlString.Equals shared/static method:
Two empty strings=True
String.Equals instance method:
Two empty strings=True