共用方式為


處理 Null 值 (ADO.NET)

更新: November 2007

當資料行中的值未知或遺失時,便會使用關聯式資料庫中的 Null 值。 Null 既不是空字串 (針對字元或 datetime 資料型別),也不是零值 (針對數值資料型別)。 根據 ANSI SQL-92 規格的內容,對所有的資料型別而言,Null 必須都是相同的,以便可一致處理所有的 Null。 藉由實作 INullable 介面,System.Data.SqlTypes 命名空間可以提供 Null 語意。 System.Data.SqlTypes 中的每個資料型別都具有自己的 IsNull 屬性及 Null 值,而該值可以指派給該資料型別的執行個體 (Instance)。

注意事項:

.NET Framework 2.0 版開始支援可為 Null 型別,這讓程式設計人員得以擴充實值型別,以表示基礎型別所有的值。 這些 CLR 可為 Null 的型別代表 Nullable 結構的執行個體。 當實質型別為 boxed 和 unboxed 時,這項功能特別有用,可強化與物件型別的相容性。 CLR 可為 Null 的型別不適用於儲存資料庫 Null,因為 ANSI SQL Null 的行為方式與 null 參考不同 (在 Visual Basic 中為 Nothing)。 若要使用資料庫 ANSI SQL Null 值,請利用 System.Data.SqlTypes Null 而非 Nullable。 如需在 Visual Basic 中使用 CLR 可為 Null 型別的詳細資訊,請參閱可為 Null 的實值型別,而若為 C#,請參閱使用可為 Null 的型別 (C# 程式設計手冊)

Null 及三種值的邏輯

在資料行定義中允許 Null 值會將三種值的邏輯引進應用程式。 比較可評估為下列三個條件之一:

  • True

  • False

  • Unknown

因為 Null 會視為 Unknown,所以比較兩個 Null 值時,並不會視為相等的。 在使用算術運算子的運算式中,如果有任何運算元為 Null,其結果也會是 Null。

Null 及 SqlBoolean

任何 System.Data.SqlTypes 之間的比較都將傳回 SqlBoolean。 每個 SqlType 的 IsNull 函式都會傳回 SqlBoolean,並可用於檢查是否有 Null 值。 下列 True 值資料表顯示存在 Null 值時,AND、OR 及 NOT 運算子將如何運作。 (T=true、F=false 及 U=unknown 或 Null)。

事實資料表

瞭解 ANSI_NULLS 選項

System.Data.SqlTypes 提供的語意與在 SQL Server 中設定 ANSI_NULLS 選項時的語意相同。 如果任何運算元或引數 (除了屬性 IsNull 之外) 為 Null,則所有算術運算子 (+、-、*、/、%)、位元運算子 (~、&、|) 及大多數函式都會傳回 Null。

ANSI SQL-92 標準不支援 WHERE 子句中的 columnName = NULL。 在 SQL Server 中,ANSI_NULLS 選項可控制資料庫中的預設 Null 屬性及針對 Null 值的比較評估。 如果啟用 ANSI_NULLS (預設值),則當測試 Null 值時,必須在運算式中使用 IS NULL 運算子。 例如,當啟用 ANSI_NULLS 時,下列比較永遠會產生 Unknown:

            colname > NULL

對包含 Null 值的變數進行比較也會產生 Unknown:

            colname > @MyVariable

使用 IS NULL 或 IS NOT NULL 述詞來測試 Null 值。 如此會增加 WHERE 子句的複雜性。 例如,AdventureWorks Customer 資料表中的 TerritoryID 資料行允許 Null 值。 如果 SELECT 陳述式除測試其他項目之外,還要測試 Null 值,則其必須包括 IS NULL 述詞:

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

如果在 SQL Server 中將 ANSI_NULLS 設為停用,則可以建立使用相等運算子的運算式,以與 Null 進行比較。 不過,您無法阻止不同連接設定該連接的 Null 選項。 不論連接的 ANSI_NULLS 設定為何,都可以使用 IS NULL 來測試 Null 值。

在 DataSet 中不支援將 ANSI_NULLS 設為停用,因為它永遠會遵循 ANSI SQL-92 標準來處理 System.Data.SqlTypes 中的 Null 值。

指派 Null 值

Null 值較特殊,其儲存及指派語意在不同類型系統及儲存系統之間會有所不同。 Dataset 是設計為與不同類型及儲存系統搭配使用。

本節將說明 Null 語意,其可指派 Null 值給跨不同類型系統之 DataRowDataColumn

  • DBNull.Value
    此指派針對任何型別的 DataColumn 都有效。 如果型別實作 INullable,則 DBNull.Value 會強制轉型為適當的強型別 (Strongly Typed) Null 值。

  • SqlType.Null
    所有 System.Data.SqlTypes 資料型別都可實作 INullable。 如果使用隱含轉換運算子,將強型別 Null 值轉換成資料行的資料型別,指派就應該能夠順利完成。 否則,將擲回無效轉換例外狀況。

  • null
    如果 'null' 是指定之 DataColumn 資料型別的合法值,它就會強制轉型為與 INullable 型別 (SqlType.Null) 相關聯的適當 DbNull.Value 或 Null。

  • derivedUdt.Null
    若為 UDT 資料行,則永遠會依據與 DataColumn 相關聯的型別來儲存 Null。 請考量下列情況:UDT 與不實作 INullable 的 DataColumn (而其子類別實作) 相關聯。 在此情況下,如果指派了與衍生類別相關聯的強型別 Null 值,它就會儲存為不具型別的 DbNull.Value,因為 Null 儲存永遠會與 DataColumn 的資料型別一致。

注意事項:

目前在 DataSet 中不支援 Nullable<T> 或 Nullable 結構。

多個資料行 (資料列) 指派

DataTable.Add、DataTable.LoadDataRow 或其他可接受對應至資料列之 ItemArray 的 API,會將 'null' 對應至 DataColumn 的預設值。 如果陣列中的物件包含 DbNull.Value 或其強型別的對應項,則會套用上述相同規則。

此外,下列規則可套用至 DataRow.["columnName"] Null 指派的執行個體:

  1. 除了強型別 Null 資料行具有適當的強型別 Null 值之外,所有其他項目的預設 default 值都為 DbNull.Value。

  2. 在序列化為 XML 檔案期間永遠不會寫出 Null 值 (如同在 xsi:nil 中)。

  3. 在序列化為 XML 時,永遠會寫出所有非 Null 值 (包括預設值)。 這與 XSD/XML 語意不同,在 XSD/XML 語意中 Null 值 (xsi:nil) 是明確的,而預設值是隱含的 (如果不存在於 XML 中,則驗證剖析器可以從相關聯的 XSD 結構描述中取得它)。 DataTable 的情況則相反: Null 值是隱含的,而預設值則是明確的。

  4. 針對從 XML 輸入讀取之資料列的所有遺漏資料行值都會指派 NULL。 使用 NewRow 或類似方法建立的資料列會指派為 DataColumn 的預設值。

  5. IsNull 方法會針對 DbNull.Value 和 INullable.Null 傳回 true。

指派 Null 值

任何 System.Data.SqlTypes 執行個體的預設值都為 Null。

System.Data.SqlTypes 中的 Null 是型別特定的,因此無法由單一值 (如 DbNull) 表示。 請使用 IsNull 屬性來檢查 Null。

Null 值可指派給 DataColumn,如下列程式碼範例中所示: 您可直接將 Null 值指派給 SqlTypes 變數,而不會觸發例外狀況。

範例

下列程式碼範例會建立 DataTable,其包含兩個定義為 SqlInt32SqlString 的資料行。 程式碼會加入一個已知值的資料列及一個 Null 值的資料列,然後在 DataTable 中重複,以將值指派給變數並在主控台視窗中顯示輸出。

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

將 Null 值與 SqlType 及 CLR 型別進行比較

比較 Null 值時,瞭解 Equals 方法評估 System.Data.SqlTypes 中 Null 值的方式,與其處理 CLR 型別之方式相比較的差異很重要。 所有 System.Data.SqlTypesEquals 方法都是使用資料庫語意來評估 Null 值: 如果其中一個值或兩個值同時為 Null,則該比較會產生 Null。 另一方面,針對兩個 System.Data.SqlTypes 使用 CLR Equals 方法時,如果二者都為 Null,則會產生 True。 這反映出使用執行個體方法 (例如 CLR String.Equals 方法) 與使用靜態/共用方法 (SqlString.Equals) 之間的差異。

下列範例示範當針對每個方法傳遞一對 Null 值,然後傳遞一對空字串時,SqlString.Equals 方法及 String.Equals 方法之間結果的差異。

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

請參閱

其他資源

SQL Server 資料型別和 ADO.NET