Обработка значений NULLHandling Null Values

Значение NULL в реляционной базе данных используется, если значение в столбце неизвестно или отсутствует.A null value in a relational database is used when the value in a column is unknown or missing. Значение Null не является ни пустой строкой (для символьного типа данных или типа данных datetime), ни нулевым значением (для числовых типов данных).A null is neither an empty string (for character or datetime data types) nor a zero value (for numeric data types). Спецификация ANSI SQL-92 устанавливает, что значение NULL должно быть одинаково для всех типов данных, так чтобы все значения NULL обрабатывались согласованно.The ANSI SQL-92 specification states that a null must be the same for all data types, so that all nulls are handled consistently. Пространство имен System.Data.SqlTypes предоставляет семантику NULL при реализации интерфейса INullable.The System.Data.SqlTypes namespace provides null semantics by implementing the INullable interface. Каждый из типов данных в System.Data.SqlTypes имеет свое собственное свойство IsNull и значение Null, которое может быть присвоено экземпляру этого типа данных.Each of the data types in System.Data.SqlTypes has its own IsNull property and a Null value that can be assigned to an instance of that data type.

Примечание

В .NET Framework версии 2.0 введена поддержка типов данных, допускающих значения NULL, что дает программистам возможность расширить тип значения, чтобы могли быть представлены все значения базового типа.The .NET Framework version 2.0 introduced support for nullable types, which allow programmers to extend a value type to represent all values of the underlying type. Эти типы среды CLR, допускающие значения NULL, представляют экземпляр структуры Nullable.These CLR nullable types represent an instance of the Nullable structure. Такая возможность особенно полезна, если для типов значений могут устанавливаться и сниматься флажки, что обеспечивает улучшенную совместимость с типами объектов.This capability is especially useful when value types are boxed and unboxed, providing enhanced compatibility with object types. Типы среды CLR, допускающие значения NULL, не предназначены для хранения значений NULL базы данных, т. к. поведение значения NULL для ANSI SQL отличается от поведения ссылки null (или Nothing в Visual Basic).CLR nullable types are not intended for storage of database nulls because an ANSI SQL null does not behave the same way as a null reference (or Nothing in Visual Basic). Для работы со значениями NULL ANSI SQL базы данных используйте значения NULL System.Data.SqlTypes, а не Nullable.For working with database ANSI SQL null values, use System.Data.SqlTypes nulls rather than Nullable. Дополнительные сведения о работе с типами данных CLR Nullable в Visual Basic см. в разделе типы значений, C# допускающих значения NULL, а для см. в статье типы значений Nullable.For more information on working with CLR nullable types in Visual Basic see Nullable Value Types, and for C# see Nullable value types.

Значения NULL и тройственная логикаNulls and Three-Valued Logic

Разрешение использовать значения NULL в определениях столбцов вводит в приложение тройственную логику.Allowing null values in column definitions introduces three-valued logic into your application. Результатом оценки сравнения может быть одно из трех условий.A comparison can evaluate to one of three conditions:

  • TrueTrue

  • FalseFalse

  • НеизвестноUnknown

Так как значения NULL рассматриваются как неизвестные, два значения NULL, сравниваемые друг с другом, не считаются равными.Because null is considered to be unknown, two null values compared to each other are not considered to be equal. В выражениях, использующих арифметические операторы, если какие-либо из операндов равны NULL, результат также равен NULL.In expressions using arithmetic operators, if any of the operands is null, the result is null as well.

Значения NULL и SqlBooleanNulls and SqlBoolean

Сравнение каких-либо System.Data.SqlTypes возвратит SqlBoolean.Comparison between any System.Data.SqlTypes will return a SqlBoolean. Функция IsNull для каждого SqlType возвращает SqlBoolean и может использоваться для проверки значений NULL.The IsNull function for each SqlType returns a SqlBoolean and can be used to check for null values. Следующие проверки истинности демонстрируют, как операторы AND, OR и NOT функционируют в присутствии значения NULL.The following truth tables show how the AND, OR, and NOT operators function in the presence of a null value. (T=true, F=false и U=неизвестно, т.е. NULL).(T=true, F=false, and U=unknown, or null.)

Таблица истинностиTruth Table

Основные сведения о параметре ANSI_NULLSUnderstanding the ANSI_NULLS Option

System.Data.SqlTypes предоставляет ту же семантику, что и в случае, когда параметр ANSI_NULLS установлен в SQL Server.System.Data.SqlTypes provides the same semantics as when the ANSI_NULLS option is set on in SQL Server. Все арифметические операторы (+,-, *,/,%), битовые операторы (~, &, |) и большинство функций возвращают значение null, если любой из операндов или аргументов имеет значение null, за исключением IsNullсвойства.All arithmetic operators (+, -, *, /, %), bitwise operators (~, &, |), and most functions return null if any of the operands or arguments is null, except for the property IsNull.

Стандарт ANSI SQL-92 не поддерживает ColumnName = NULL в предложении WHERE.The ANSI SQL-92 standard does not support columnName = NULL in a WHERE clause. В SQL Server параметр ANSI_NULLS контролирует как допустимость значений NULL по умолчанию, так и оценку сравнений со значениями NULL.In SQL Server, the ANSI_NULLS option controls both default nullability in the database and evaluation of comparisons against null values. Если параметр ANSI_NULLS установлен в значение ON (по умолчанию), в выражениях при проверке на значения NULL должен использоваться оператор IS NULL.If ANSI_NULLS is turned on (the default), the IS NULL operator must be used in expressions when testing for null values. Например, при параметре ANSI_NULLS, установленном в значение ON, результатом следующего сравнения всегда является UNKNOWN:For example, the following comparison always yields unknown when ANSI_NULLS is on:

colname > NULL  

Результатом сравнения переменной, содержащей значение NULL, также является UNKNOWN:Comparison to a variable containing a null value also yields unknown:

colname > @MyVariable  

Для проверки на значение NULL используется предикат IS NULL или IS NOT NULL.Use the IS NULL or IS NOT NULL predicate to test for a null value. Это может усложнить предложение WHERE.This can add complexity to the WHERE clause. Например, в столбце TerritoryID таблицы «Заказчики AdventureWorks» разрешены значения NULL.For example, the TerritoryID column in the AdventureWorks Customer table allows null values. Если инструкция SELECT используется для проверки на значения NULL в дополнение к другим, она должна включать предикат IS NULL:If a SELECT statement is to test for null values in addition to others, it must include an IS NULL predicate:

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

Если в SQL Server параметр ANSI_NULLS установлен в OFF, можно создать выражения, использующие оператор равенства для сравнения со значением NULL.If you set ANSI_NULLS off in SQL Server, you can create expressions that use the equality operator to compare to null. Однако нельзя предотвратить в разных соединениях установку параметров NULL для данного соединения.However, you can't prevent different connections from setting null options for that connection. Использование IS NULL для проверки на значения NULL всегда работает, независимо от установки ANSI_NULLS для соединения.Using IS NULL to test for null values always works, regardless of the ANSI_NULLS settings for a connection.

Установка ANSI_NULLS в OFF не поддерживается в DataSet, который всегда следует стандарту ANSI SQL-92 для обработки значений NULL в System.Data.SqlTypes.Setting ANSI_NULLS off is not supported in a DataSet, which always follows the ANSI SQL-92 standard for handling null values in System.Data.SqlTypes.

Присвоение значений NullAssigning Null Values

Значения NULL являются особыми, и их семантика хранения и присвоения различается для разных систем типов и систем хранения.Null values are special, and their storage and assignment semantics differ across different type systems and storage systems. Dataset предназначен для использования с различными системами типов и хранения.A Dataset is designed to be used with different type and storage systems.

В этом разделе описывается семантика присвоения значений NULL для DataColumn в DataRow для разных систем типов.This section describes the null semantics for assigning null values to a DataColumn in a DataRow across the different type systems.

DBNull.Value
Это присвоение действительно для DataColumn любого типа.This assignment is valid for a DataColumn of any type. Если тип реализует INullable, DBNull.Value приводится в соответствующее строго типизированное значение NULL.If the type implements INullable, DBNull.Value is coerced into the appropriate strongly typed Null value.

SqlType.Null
Все типы данных System.Data.SqlTypes реализуют INullable.All System.Data.SqlTypes data types implement INullable. Если строго типизированное значение NULL может быть преобразовано в тип данных столбца с использованием неявных операторов приведения, присвоение должно выполняться.If the strongly typed null value can be converted into the column's data type using implicit cast operators, the assignment should go through. В противном случае возникает исключение недопустимого приведения.Otherwise an invalid cast exception is thrown.

null
Если NULL является допустимым значением для типа данных данного DataColumn, оно приводится в соответствующее DbNull.Value или Null, связанное с INullable типом (SqlType.Null)If 'null' is a legal value for the given DataColumn data type, it is coerced into the appropriate DbNull.Value or Null associated with the INullable type (SqlType.Null)

derivedUdt.Null
Для столбцов определяемого пользователем типа значения NULL всегда сохраняются на основании типа, связанного с DataColumn.For UDT columns, nulls are always stored based on the type associated with the DataColumn. Рассмотрим случай определяемого пользователем типа, связанного с DataColumn, который не реализует INullable, хотя его подкласс реализует.Consider the case of a UDT associated with a DataColumn that does not implement INullable while its sub-class does. В этом случае, если присваивается значение NULL со строгой типизацией, которое связано с производным классом, оно хранится как нетипизированное DbNull.Value, т. к. хранение значения NULL всегда согласовано с типом данных DataColumn.In this case, if a strongly typed null value associated with the derived class is assigned, it is stored as an untyped DbNull.Value, because null storage is always consistent with the DataColumn's data type.

Примечание

Структура Nullable<T> или Nullable в настоящее время в DataSet не поддерживается.The Nullable<T> or Nullable structure is not currently supported in the DataSet.

Присвоение для многих столбцов или строкMultiple Column (Row) Assignment

DataTable.Add, DataTable.LoadDataRow или другие API, которые принимают ItemArray, которые сопоставлены со строкой, сопоставляют «NULL» значению по умолчанию для DataColumn.DataTable.Add, DataTable.LoadDataRow, or other APIs that accept an ItemArray that gets mapped to a row, map 'null' to the DataColumn's default value. Если объект в массиве содержит DbNull.Value или его строго типизированный аналог, применяются те же вышеописанные правила.If an object in the array contains DbNull.Value or its strongly typed counterpart, the same rules as described above are applied.

Кроме того, следующие правила применяются для экземпляра присваивания DataRow.["columnName"] значений NULL.In addition, the following rules apply for an instance of DataRow.["columnName"] null assignments:

  1. Значение по умолчанию DbNull.Value для всех, кроме строго типизированных столбцов NULL, где это строго типизированное значение null.The default default value is DbNull.Value for all except the strongly typed null columns where it is the appropriate strongly typed null value.

  2. Значения NULL никогда не записываются в XML-файлы во время сериализации (в виде "xsi:nil").Null values are never written out during serialization to XML files (as in "xsi:nil").

  3. Все отличные от NULL значения, включая значения по умолчанию, при сериализации всегда записываются в XML.All non-null values, including defaults, are always written out while serializing to XML. Это отличается от семантики XSD и XML, где значение NULL (xsi:nil) является явным, а значение по умолчанию - неявным (если оно не присутствует в XML, проверяющее средство синтаксического анализа может получить его из связанной схемы XSD).This is unlike XSD/XML semantics where a null value (xsi:nil) is explicit and the default value is implicit (if not present in XML, a validating parser can get it from an associated XSD schema). Для DataTable справедливо и обратное: значение NULL является неявным, а значение по умолчанию - явным.The opposite is true for a DataTable: a null value is implicit and the default value is explicit.

  4. Всем отсутствующим значениям столбцов для строк, считываемым из входных XML-данных, присваивается значение NULL.All missing column values for rows read from XML input are assigned NULL. Строкам, созданным методом NewRow или аналогичным методом, присваивается значение по умолчанию для DataColumn.Rows created using NewRow or similar methods are assigned the DataColumn's default value.

  5. Метод IsNull возвращает true и для DbNull.Value, и для INullable.Null.The IsNull method returns true for both DbNull.Value and INullable.Null.

Присвоение значений NullAssigning Null Values

Аргументом по умолчанию для любого экземпляра System.Data.SqlTypes является значение NULL.The default value for any System.Data.SqlTypes instance is null.

Значения NULL в System.Data.SqlTypes зависят от типа и не могут представляться одним значением, таким как DbNull.Nulls in System.Data.SqlTypes are type-specific and cannot be represented by a single value, such as DbNull. Для проверки на значения NULL используется свойство IsNull.Use the IsNull property to check for nulls.

Значения NULL могут присваиваться DataColumn, как показано в следующем примере кода.Null values can be assigned to a DataColumn as shown in the following code example. Значения NULL можно непосредственно присваивать переменным SqlTypes без возникновения исключения.You can directly assign null values to SqlTypes variables without triggering an exception.

ПримерExample

В следующем примере кода создается DataTable с двумя столбцами, определенными как SqlInt32 и SqlString.The following code example creates a DataTable with two columns defined as SqlInt32 and SqlString. Код добавляет одну строку известных значений, одну строку значений NULL и затем производит итерацию по DataTable, присваивая значения переменным и отображая выходные данные в окне консоли.The code adds one row of known values, one row of null values and then iterates through the DataTable, assigning the values to variables and displaying the output in the console window.

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

В результате выполнения данного примера выдаются следующие результаты:This example displays the following results:

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

Сравнение значений NULL с типами SqlType и CLRComparing Null Values with SqlTypes and CLR Types

При сравнении значений NULL важно понимать разницу между способом, которым метод Equals оценивает значения NULL в System.Data.SqlTypes, и способом его работы с типами среды CLR.When comparing null values, it is important to understand the difference between the way the Equals method evaluates null values in System.Data.SqlTypes as compared with the way it works with CLR types. Все методы System.Data.SqlTypesEquals используют семантику базы данных для оценки значений NULL: если одно из значений или оба значения равны NULL, то результатом сравнения является NULL.All of the System.Data.SqlTypesEquals methods use database semantics for evaluating null values: if either or both of the values is null, the comparison yields null. С другой стороны, результатом использования метода среды CLR Equals на двух System.Data.SqlTypes является true, если оба значения являются NULL.On the other hand, using the CLR Equals method on two System.Data.SqlTypes will yield true if both are null. Это отражает разницу между использованием метода экземпляра, например метода среды CLR String.Equals, и использованием статического или общего метода SqlString.Equals.This reflects the difference between using an instance method such as the CLR String.Equals method, and using the static/shared method, SqlString.Equals.

Следующий пример демонстрирует разницу в результатах методов SqlString.Equals и String.Equals, если каждому посылается пара значений NULL, а затем пара пустых строк.The following example demonstrates the difference in results between the SqlString.Equals method and the String.Equals method when each is passed a pair of null values and then a pair of empty strings.

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

Этот код выводит следующие результаты:The code produces the following output:

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   

См. такжеSee also