Behandlung von NULL-Werten (ADO.NET)

Aktualisiert: November 2007

Wenn der Wert in einer Spalte unbekannt ist oder fehlt, wird in einer relationalen Datenbank ein NULL-Wert verwendet. NULL ist weder eine leere Zeichenfolge (für Zeichen- oder Datetime-Datentypen) noch ein Wert 0 (null) (für numerische Datentypen). Die ANSI SQL-92-Spezifikation legt fest, dass NULL für alle Datentypen gleich sein muss, sodass alle NULL-Werte einheitlich behandelt werden können. Der System.Data.SqlTypes-Namespace stellt durch Implementieren der INullable-Schnittstelle eine NULL-Semantik bereit. Jeder Datentyp in System.Data.SqlTypes besitzt eine eigene IsNull-Eigenschaft und einen Null-Wert. Diese können einer Instanz dieses Datentyps zugewiesen werden.

Hinweis:

Neu in .NET Framework 2.0 ist die Unterstützung für Typen, die NULL-Werte zulassen. Programmierer können damit einen Werttyp so erweitern, dass dieser alle Werte des zugrunde liegenden Typs darstellen kann. Die CLR-Typen, die NULL-Werte zulassen, stellen eine Instanz der Nullable-Struktur dar. Diese Funktion erweist sich vor allem dann als hilfreich, wenn Werttypen geschachtelt und nicht geschachtelt sind, wodurch sich die Kompatibilität mit Objekttypen verbessert. CLR-Typen, die NULL-Werte zulassen, sind nicht zum Speichern von Datenbank-NULL-Werten gedacht, weil sich ein ANSI-SQL-NULL-Wert anders als +++ein null (oder Nothing in Visual Basic)+++ verhält. Verwenden Sie zum Arbeiten mit ANSI SQL-Datenbank-NULL-Werten statt Nullable lieber System.Data.SqlTypes-NULL-Werte. Weitere Informationen zum Arbeiten mit CLR-Typen, die NULL-Werte zulassen, finden Sie unter Auf NULL festlegbare Werttypen (für Visual Basic) bzw. Verwenden von auf NULL festlegbaren Typen (C# Programmierhandbuch) (für C#)..

NULL-Werte und dreiwertige Logik

Wenn in Spaltendefinitionen NULL-Werte zugelassen werden, wird dreiwertige Logik in die Anwendung eingeführt. Eine Vergleichsoperation wird mit einer der drei nachfolgenden Bedingungen ausgewertet:

  • True

  • False

  • Unknown (Unbekannt)

Da NULL als unbekannt betrachtet wird, gelten zwei miteinander verglichene NULL-Werte nicht als gleich. In Ausdrücken, die arithmetische Operatoren verwenden, ist das Ergebnis NULL, wenn einer der Operanden NULL ist.

NULL-Werte und SqlBoolean

Ein Vergleich zwischen beliebigen System.Data.SqlTypes gibt einen SqlBoolean zurück. Die IsNull-Funktion für jeden SqlType gibt einen SqlBoolean-Wert zurück und kann dazu verwendet werden, das Vorhandensein von NULL-Werten zu überprüfen. Den folgenden Tabellen mit Wahrheitswerten können Sie entnehmen, wie die Operatoren AND, OR und NOT bei Vorhandensein eines NULL-Werts funktionieren. (T=true (wahr), F=false (falsch) und U=unknown (unbekannt) oder NULL.)

Wahrheitstabelle

Informationen zur ANSI_NULLS-Option

Mit System.Data.SqlTypes wird dieselbe Semantik bereitgestellt wie bei aktivierter ANSI_NULLS-Option in SQL Server. Alle arithmetischen Operatoren (+, -, *, /, %), bitweisen Operatoren (~, &, |) und die meisten Funktionen geben NULL zurück, wenn einer der Operanden oder Argumente NULL ist, außer für die Eigenschaft IsNull.

Der ANSI SQL-92-Standard unterstützt columnName = NULL in einer WHERE-Klausel nicht. In SQL Server steuert die ANSI_NULLS-Option die Fähigkeit, in der Standardeinstellung NULL-Werte in der Datenbank zuzulassen sowie die Auswertung von Vergleichen mit NULL-Werten. Wenn ANSI_NULLS aktiviert ist (Standardeinstellung), muss der IS NULL-Operator beim Testen auf NULL-Werte in Ausdrücken verwendet werden. Der folgende Vergleich ergibt z. B. immer "Unknown", wenn ANSI_NULLS aktiviert ist:

            colname > NULL

Der Vergleich mit einer Variablen, die einen NULL-Wert enthält, ergibt auch "Unknown":

            colname > @MyVariable

Verwenden Sie zum Testen auf einen NULL-Wert das IS NULL-Prädikat oder das IS NOT NULL-Prädikat. Dies kann die Komplexität der WHERE-Klausel erhöhen. Die TerritoryID-Spalte in der AdventureWorks Customer-Tabelle lässt z. B. NULL-Werte zu. Wenn eine SELECT-Anweisung zusätzlich zu anderen Werten auf NULL-Werte getestet werden soll, muss sie ein IS NULL-Prädikat enthalten:

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

Wenn Sie in SQL Server ANSI_NULLS deaktivieren, können Sie Ausdrücke erstellen, die mithilfe des Gleichheitsoperators einen Vergleich mit NULL durchführen. Sie können jedoch nicht verhindern, dass verschiedene Verbindungen NULL-Optionen für diese Verbindung festlegen. Die Verwendung von IS NULL funktioniert zum Testen auf NULL-Werte immer, unabhängig von den für eine Verbindung festgelegten ANSI_NULLS-Einstellungen.

Die Deaktivierung von ANSI_NULLS wird in einem DataSet nicht unterstützt. Dieses befolgt immer den ANSI SQL-92-Standard für die Behandlung von NULL-Werten in System.Data.SqlTypes.

Zuweisen von NULL-Werten

NULL-Werte weisen einige Besonderheiten auf und ihre Speicherung und Zuweisungssemantik ist in verschiedenen Typ- und Speichersystemen unterschiedlich. Ein Dataset ist für die Verwendung mit verschiedenen Typ- und Speichersystemen vorgesehen.

In diesem Abschnitt wird die NULL-Semantik zum Zuweisen von NULL-Werten zu einer DataColumn in einer DataRow in den verschiedenen Typsystemen beschrieben.

  • DBNull.Value
    Diese Zuweisung ist für eine DataColumn eines beliebigen Typs gültig. Wenn der Typ INullable implementiert, wird DBNull.Value in den entsprechenden stark typisierten NULL-Wert umgewandelt.

  • SqlType.Null
    Alle System.Data.SqlTypes-Datentypen implementieren INullable. Wenn der stark typisierte NULL-Wert mit impliziten Umwandlungsoperatoren in den Datentyp der Spalte umgewandelt werden kann, sollte die Zuweisung durchgeführt werden können. Andernfalls wird eine Ausnahme für eine ungültige Umwandlung ausgelöst.

  • null
    Wenn 'NULL' ein zulässiger Wert für den angegebenen DataColumn-Datentyp ist, wird er in den entsprechenden DbNull.Value oder Null umgewandelt, der dem INullable-Typ (SqlType.Null) zugeordnet ist.

  • derivedUdt.Null
    Bei UDT-Spalten werden NULL-Werte immer auf der Grundlage des Typs gespeichert, der der DataColumn zugeordnet ist. Nehmen wir einen UDT, der einer DataColumn zugeordnet ist und der INullable im Gegensatz zu seiner Unterklasse nicht implementiert. In diesem Fall wird er als nicht typisierter DbNull.Value gespeichert, wenn ein stark typisierter NULL-Wert zugewiesen wird, der der abgeleiteten Klasse zugeordnet ist, da die Speicherung von NULL-Werten immer mit dem Datentyp der DataColumn konsistent ist.

Hinweis:

Die Nullable<T>-Struktur und die Nullable-Struktur werden im DataSet derzeit nicht unterstützt.

Zuweisung mehrerer Spalten (Zeilen)

DataTable.Add, DataTable.LoadDataRow oder andere APIs, die ein ItemArray akzeptieren, das einer Reihe zugeordnet wird, ordnen dem Standardwert der <legacyBold>DataColumn</legacyBold> 'NULL' zu. Wenn ein Objekt im Array DbNull.Value oder dessen stark typisierte Entsprechung enthält, gelten dieselben Regeln wie oben beschrieben.

Darüber hinaus gelten für Instanzen von DataRow.["columnName"]-NULL-Zuweisungen die folgenden Regeln:

  1. Für alle Spalten (mit Ausnahme der stark typisierten NULL-Spalten) lautet der default-Standardwert DbNull.Value. In den stark typisierten NULL-Spalten ist es der entsprechende stark typisierte NULL-Wert.

  2. NULL-Werte werden bei der Serialisierung in XML-Dateien (wie in "xsi:nil") nie ausgegeben.

  3. Alle Nicht-NULL-Werte einschließlich der Standardwerte werden bei der Serialisierung nach XML immer ausgegeben. Dies entspricht nicht der XSD/XML-Semantik, bei der ein NULL-Wert (xsi:nil) explizit und der Standardwert implizit ist (wenn nicht in XML vorhanden, kann der Wert von einem validierenden Parser aus einem zugeordneten XSD-Schema abgerufen werden). Das Gegenteil gilt für eine DataTable: Ein NULL-Wert ist implizit, und der Standardwert ist explizit.

  4. Allen fehlenden Spaltenwerten für Zeilen, die aus der XML-Eingabe gelesen werden, wird NULL zugewiesen. Zeilen, die mithilfe von NewRow oder ähnlichen Methoden erstellt wurden, wird der Standardwert von DataColumn zugewiesen.

  5. Die IsNull-Methode gibt sowohl für DbNull.Value als auch für INullable.Nulltrue zurück.

Zuweisen von NULL-Werten

Der Standardwert für alle System.Data.SqlTypes-Instanzen ist NULL.

NULL-Werte in System.Data.SqlTypes sind typspezifisch und können nicht von einem einzelnen Wert (z. B. DbNull) dargestellt werden. Mit der IsNull-Eigenschaft können Sie überprüfen, ob NULL-Werte vorliegen.

NULL-Werte können einer DataColumn wie im folgenden Codebeispiel dargestellt zugewiesen werden. Sie können SqlTypes-Variablen direkt NULL-Werte zuweisen, ohne eine Ausnahme auszulösen.

Beispiel

Im folgenden Codebeispiel wird eine DataTable mit zwei Spalten erstellt, die als SqlInt32 und SqlString definiert werden. Der Code fügt eine Zeile mit bekannten Werten und eine Zeile mit NULL-Werten hinzu und durchläuft dann die DataTable. Dabei werden den Variablen die Werte zugewiesen und die Ausgabe im Konsolenfenster angezeigt.

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

In diesem Beispiel werden die folgenden Ergebnisse angezeigt:

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

Vergleichen von NULL-Werten mit "SqlTypes" und CLR-Typen

Beim Vergleichen von NULL-Werten ist es wichtig zu verstehen, wie die Equals-Methode NULL-Werte in System.Data.SqlTypes auswertet und wie sie im Unterschied dazu bei CLR-Typen vorgeht. Alle System.Data.SqlTypesEquals-Methoden verwenden für die Auswertung von NULL-Werten die Datenbanksemantik: Wenn mindestens einer der Werte NULL ist, ergibt der Vergleich NULL. Andererseits ergibt die Anwendung der CLR-Equals-Methode auf zwei System.Data.SqlTypes <legacyBold>true</legacyBold>, wenn beide NULL sind. Dies spiegelt den Unterschied zwischen der Verwendung einer Instanzmethode wie der CLR-String.Equals-Methode und der Verwendung der <legacyBold>static</legacyBold>/<legacyBold>shared</legacyBold>-Methode SqlString.Equals wider.

Das folgende Beispiel zeigt den Unterschied in den Ergebnissen zwischen der SqlString.Equals-Methode und der String.Equals-Methode, wenn beiden Methoden ein Paar von NULL-Werten und dann ein Paar leerer Zeichenfolgen übergeben wird.

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;
    }
}

Der Code erzeugt die folgende Ausgabe:

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

Siehe auch

Weitere Ressourcen

SQL Server-Datentypen und ADO.NET