Udostępnij za pośrednictwem


Obsługa wartości null

Wartość null w relacyjnej bazie danych jest używana, gdy wartość w kolumnie jest nieznana lub brakuje jej. Wartość null nie jest ciągiem pustym (dla typów danych znaków lub daty/godziny) ani wartością zerową (dla typów danych liczbowych). Specyfikacja ANSI SQL-92 stwierdza, że wartość null musi być taka sama dla wszystkich typów danych, dzięki czemu wszystkie wartości null są obsługiwane spójnie. System.Data.SqlTypes Przestrzeń nazw zapewnia semantykę null poprzez zaimplementowanie interfejsu INullable. Każdy z typów danych w System.Data.SqlTypes ma własną IsNull właściwość i wartość Null, którą można przypisać do wystąpienia tego typu danych.

Uwaga / Notatka

Program .NET Framework w wersji 2.0 wprowadził obsługę typów wartości dopuszczających wartość null, co umożliwia programistom rozszerzenie typu wartości w celu reprezentowania wszystkich wartości bazowego typu. Typy wartości dopuszczalne CLR reprezentują wystąpienie struktury Nullable. Ta funkcja jest szczególnie przydatna, gdy typy wartości są opakowywane i odpakowywane, co zapewnia lepszą zgodność z typami obiektów. Typy wartości dopuszczające wartość null w CLR nie są przeznaczone do przechowywania wartości null bazy danych, ponieważ wartość null w ANSI SQL nie zachowuje się tak samo jak referencja null (lub Nothing w Visual Basic). Aby pracować z wartościami null baz danych SQL ANSI, użyj System.Data.SqlTypes wartości null, a nie Nullable. Aby uzyskać więcej informacji na temat pracy z typami wartości nullable CLR w języku Visual Basic, zobacz Typy wartości nullable, a w przypadku języka C# zobacz Typy wartości nullable.

Wartości null i logika Three-Valued

Zezwalanie na wartości null w definicjach kolumn wprowadza do aplikacji trzywartą logikę. Porównanie może ocenić jeden z trzech warunków:

  • Prawda

  • Nieprawda

  • Nieznany

Ponieważ wartość null jest uważana za nieznaną, dwie wartości null w porównaniu ze sobą nie są uważane za równe. W wyrażeniach używających operatorów arytmetycznych, jeśli którykolwiek z operandów ma wartość null, wynik ma również wartość null.

Wartości null i SqlBoolean

Porównanie między dowolnym System.Data.SqlTypes elementem zwróci wartość SqlBoolean. Funkcja IsNull dla każdego SqlType zwraca SqlBoolean i może służyć do sprawdzania wartości null. W poniższych tabelach prawdy pokazano, jak operatory AND, OR i NOT działają w obecności wartości null. (T=true, F=false i U=unknown lub null).

Tabela prawdy

Opis opcji ANSI_NULLS

System.Data.SqlTypes udostępnia te same semantyki co w przypadku ustawienia opcji ANSI_NULLS w programie SQL Server. Wszystkie operatory arytmetyczne (+, -, *, /, %), operatory bitowe (~, &, |) i większość funkcji zwracają wartość null, jeśli którykolwiek z operandów lub argumentów ma wartość null, z wyjątkiem właściwości IsNull.

Standard ANSI SQL-92 nie obsługuje kolumny columnName = NULL w klauzuli WHERE. W programie SQL Server opcja ANSI_NULLS kontroluje zarówno domyślną wartość null w bazie danych, jak i ocenę porównań z wartościami null. Jeśli ANSI_NULLS jest włączona (wartość domyślna), operator IS NULL musi być używany w wyrażeniach podczas testowania wartości null. Na przykład następujące porównanie zawsze zwraca wartość nieznaną, gdy ANSI_NULLS jest włączona:

colname > NULL  

Porównanie ze zmienną zawierającą wartość null również daje rezultat nieznany.

colname > @MyVariable  

Użyj predykatu IS NULL lub IS NOT NULL, aby przetestować wartość null. Może to zwiększyć złożoność klauzuli WHERE. Na przykład kolumna TerritoryID w tabeli AdventureWorks Customer zezwala na wartości null. Jeśli instrukcja SELECT ma testować wartości null oprócz innych, musi zawierać predykat IS NULL:

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

Jeśli ustawisz ANSI_NULLS wyłączone w programie SQL Server, możesz utworzyć wyrażenia, które używają operatora równości do porównania z wartością null. Nie można jednak uniemożliwić innym połączeniom ustawiania opcji null dla tego połączenia. Używanie IS NULL do sprawdzenia wartości null zawsze jest skuteczne, niezależnie od ustawień ANSI_NULLS dla połączenia.

Ustawienie opcji ANSI_NULLS na wyłączone nie jest obsługiwane w DataSet, który zawsze przestrzega standardu ANSI SQL-92 do obsługi wartości null w System.Data.SqlTypes.

Przypisywanie wartości null

Wartości null są szczególne, a ich semantyka przechowywania i przypisywania różni się w zależności od różnych systemów typów i systemów przechowywania. Element jest Dataset przeznaczony do użycia z różnymi typami i systemami przechowywania.

W tej sekcji opisano semantykę wartości null przy przypisywaniu wartości null do elementu DataColumn w różnych systemach typów DataRow.

DBNull.Value
To przypisanie jest prawidłowe dla DataColumn dowolnego typu. Jeśli typ implementuje INullable, DBNull.Value jest przekształcany w odpowiednią silnie typizowaną wartość null.

SqlType.Null
Wszystkie System.Data.SqlTypes typy danych implementują INullable. Jeśli wartość null o silnym typowaniu może być przekonwertowana na typ danych kolumny przy użyciu operatorów rzutowania niejawnych, przypisanie powinno zostać wykonane. W przeciwnym razie jest zgłaszany nieprawidłowy wyjątek rzutu.

null
Jeśli wartość "null" jest wartością prawną dla danego DataColumn typu danych, jest ona zmuszana do odpowiedniego DbNull.Value lub Null skojarzonego z typem INullable (SqlType.Null)

derivedUdt.Null
W przypadku kolumn UDT wartości null są zawsze przechowywane zgodnie z typem skojarzonym z DataColumn. Rozważmy przypadek UDT skojarzonego z elementem DataColumn, który nie implementuje INullable, podczas gdy jego podklasa to robi. W takim przypadku, jeśli silnie typizowana wartość null połączona z klasą pochodną jest przypisana, jest przechowywana jako nietypizowana DbNull.Value, ponieważ przechowywanie wartości null jest zawsze zgodne z typem danych DataColumn.

Uwaga / Notatka

Struktura Nullable<T> lub Nullable nie jest obecnie obsługiwana w pliku DataSet.

Wartość domyślna dla dowolnego System.Data.SqlTypes wystąpienia ma wartość null.

Wartości null w obiekcie System.Data.SqlTypes są specyficzne dla typu i nie mogą być reprezentowane przez jedną wartość, taką jak DbNull. Użyj właściwości , IsNull aby sprawdzić, czy nie ma wartości null.

Wartości null można przypisać do elementu , DataColumn jak pokazano w poniższym przykładzie kodu. Możesz bezpośrednio przypisać wartości null do SqlTypes zmiennych bez wyzwalania wyjątku.

Przykład

Poniższy przykład kodu tworzy obiekt DataTable z dwiema kolumnami zdefiniowanymi jako SqlInt32 i SqlString. Kod dodaje jeden wiersz znanych wartości, jeden wiersz wartości null, a następnie iteruje za pośrednictwem DataTable, przypisując wartości do zmiennych i wyświetlając dane wyjściowe w oknie konsoli.

static void WorkWithSqlNulls()
{
    DataTable table = new();

    // 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();
    }
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

W tym przykładzie zostaną wyświetlone następujące wyniki:

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

Przypisanie wielu kolumn (wierszy)

DataTable.Add, DataTable.LoadDataRow, lub inne interfejsy API, które akceptują element ItemArray, który jest przypisywany do wiersza, przypisują wartość 'null' na wartość domyślną kolumny DataColumn. Jeśli obiekt w tablicy zawiera DbNull.Value lub jego silnie typizowane odpowiedniki, stosowane są te same reguły, co opisane powyżej.

Ponadto dla wystąpienia DataRow.["columnName"] przypisań o wartości null obowiązują następujące reguły:

  1. Wartość domyślna dotyczy DbNull.Value wszystkich z wyjątkiem silnie typizowanych kolumn null, gdzie jest to odpowiednia silnie typizowana wartość null.

  2. Wartości null nigdy nie są zapisywane podczas serializacji do plików XML (jak w pliku "xsi:nil").

  3. Wszystkie wartości inne niż null, w tym wartości domyślne, są zawsze zapisywane podczas serializacji do formatu XML. Jest to w przeciwieństwie do semantyki XSD/XML, gdzie wartość null (xsi:nil) jest jawna, a wartość domyślna jest niejawna (jeśli nie istnieje w formacie XML, analizator sprawdzania poprawności może pobrać go ze skojarzonego schematu XSD). Jest odwrotnie w przypadku DataTable: wartość null jest niejawna, a wartość domyślna jest jawna.

  4. Wszystkie brakujące wartości kolumn dla wierszy odczytanych z danych wejściowych XML mają przypisaną wartość NULL. Do wartości domyślnej kolumny DataColumn są przypisywane wiersze utworzone przy użyciu NewRow lub podobne metody.

  5. Metoda IsNull zwraca true zarówno dla DbNull.Value, jak i dla INullable.Null.

Porównywanie wartości null z typami SqlTypes i CLR

Podczas porównywania wartości null ważne jest, aby zrozumieć różnicę między sposobem, w jaki metoda Equals ocenia wartości null w System.Data.SqlTypes, a sposobem, w jaki działa z typami CLR. System.Data.SqlTypes Equals Wszystkie metody używają semantyki bazy danych do obliczania wartości null: jeśli jedna lub obie wartości mają wartość null, porównanie daje wartość null. Z drugiej strony użycie metody CLR Equals na dwóch System.Data.SqlTypes zwróci wartość true, jeśli oba mają wartość null. Odzwierciedla to różnicę między użyciem metody wystąpienia, takiej jak metoda CLRString.Equals, a użyciem metody statycznej/udostępnionej. SqlString.Equals

W poniższym przykładzie pokazano różnicę w wynikach między metodą SqlString.Equals a metodą String.Equals, gdy każda z nich otrzymuje parę wartości null, a następnie parę pustych ciągów znaków.

    static void CompareNulls()
    {
        // Create two new null strings.
        SqlString a = new();
        SqlString b = new();

        // Compare nulls using static/shared SqlString.Equals.
        Console.WriteLine("SqlString.Equals shared/static method:");
        Console.WriteLine($"  Two nulls={SqlStringEquals(a, b)}");

        // Compare nulls using instance method String.Equals.
        Console.WriteLine();
        Console.WriteLine("String.Equals instance method:");
        Console.WriteLine($"  Two nulls={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={SqlStringEquals(a, b)}");

        Console.WriteLine();
        Console.WriteLine("String.Equals instance method:");
        Console.WriteLine($"  Two empty strings={StringEquals(a, b)}");
    }

    static string SqlStringEquals(SqlString string1, SqlString string2)
    {
        // SqlString.Equals uses database semantics for evaluating nulls.
        var returnValue = SqlString.Equals(string1, string2).ToString();
        return returnValue;
    }

    static string StringEquals(SqlString string1, SqlString string2)
    {
        // String.Equals uses CLR type semantics for evaluating nulls.
        var returnValue = string1.Equals(string2).ToString();
        return returnValue;
    }
}
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

Kod generuje następujące dane wyjściowe:

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

Zobacz także