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ę o wartości null przez zaimplementowanie interfejsuINullable. Każdy z typów danych w programie System.Data.SqlTypes ma własną IsNullNull właściwość i wartość, którą można przypisać do wystąpienia tego typu danych.

Uwaga

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. Te typy wartości dopuszczalnych do wartości CLR reprezentują wystąpienie Nullable struktury. Ta funkcja jest szczególnie przydatna, gdy typy wartości są w pudełku i bez skrzynki odbiorczej, co zapewnia lepszą zgodność z typami obiektów. Typy wartości dopuszczanych przez clR nie są przeznaczone do przechowywania wartości null bazy danych, ponieważ wartość null ANSI SQL nie zachowuje się tak samo jak null odwołanie (lub Nothing w Visual Basic). Do pracy z wartościami null SQL ANSI bazy danych użyj System.Data.SqlTypes wartości null, a nie Nullable. Aby uzyskać więcej informacji na temat pracy z typami dopuszczanymi wartości clR w języku Visual Basic, zobacz Typy wartości dopuszczalnych do wartości null, a w przypadku języka C# zobacz Typy wartości dopuszczanych do wartości null.

Wartości null i logika z trzema wartościami

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

  • Prawda

  • Fałsz

  • Nieznane

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 wartość i SqlBoolean 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).

Truth Table

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 zwraca wartość null, jeśli którykolwiek z argumentó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 daje również nieznane:

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 wartości NULL IS do testowania wartości null zawsze działa, niezależnie od ANSI_NULLS ustawień połączenia.

Ustawienie ANSI_NULLS wyłączone nie jest obsługiwane w obiekcie , który zawsze jest zgodny ze standardem DataSetANSI SQL-92 do obsługi wartości null w programie System.Data.SqlTypes.

Przypisywanie wartości null

Wartości null są specjalne, a ich semantyka magazynu i przypisania różni się w różnych systemach typów i systemach magazynowania. Element jest Dataset przeznaczony do użycia z różnymi typami i systemami magazynowania.

W tej sekcji opisano semantyka o wartościach null na potrzeby przypisywania wartości null do elementu DataColumn w DataRow różnych systemach typów.

DBNull.Value
To przypisanie jest prawidłowe dla DataColumn dowolnego typu. Jeśli typ implementuje INullablewartość , DBNull.Value jest zmuszany do odpowiedniej silnie typizowanej wartości null.

SqlType.Null
Wszystkie System.Data.SqlTypes typy danych implementują wartość INullable. Jeśli silnie typizowanej wartości null można przekonwertować na typ danych kolumny przy użyciu niejawnych operatorów rzutów, przypisanie powinno przejść. 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 na podstawie typu skojarzonego z .DataColumn Rozważmy przypadek udT skojarzonego z elementem DataColumn , który nie implementuje INullable , gdy jego podklasa nie działa. W takim przypadku, jeśli jest przypisana silnie typizowana wartość null skojarzona z klasą pochodną, jest przechowywana jako nietypowa DbNull.Value, ponieważ magazyn o wartości null jest zawsze zgodny z typem danych dataColumn.

Uwaga

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 (wiersz)

DataTable.Add, DataTable.LoadDataRowlub inne interfejsy API, które akceptują ItemArray element, który jest mapowany na wiersz, mapuje 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 typiowanych 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). Przeciwieństwem jest wartość true dla wartości 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 wartość zarówno dla metody , jak DbNull.Value i 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 Equals metoda ocenia wartości null w porównaniu ze sposobem, w System.Data.SqlTypes jaki działa z typami CLR. System.Data.SqlTypesEquals Wszystkie metody używają semantyki bazy danych do obliczania wartości null: jeśli albo 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 metodach zwróci wartość true, jeśli obie 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 SqlString.Equals metodą a String.Equals metodą, gdy każda z nich jest przekazywana parę wartości null, a następnie parę pustych ciągó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={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));
    }

    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 też