Обработка значений Null (ADO.NET)
Значение NULL в реляционной базе данных используется, если значение в столбце неизвестно или отсутствует. Значение Null не является ни пустой строкой (для символьного типа данных или типа данных datetime), ни нулевым значением (для числовых типов данных). Спецификация ANSI SQL-92 устанавливает, что значение NULL должно быть одинаково для всех типов данных, так чтобы все значения NULL обрабатывались согласованно. Пространство имен System.Data.SqlTypes предоставляет семантику NULL при реализации интерфейса INullable. Каждый из типов данных в System.Data.SqlTypes имеет свое собственное свойство IsNull и значение Null, которое может быть присвоено экземпляру этого типа данных.
![]() |
---|
В .NET Framework версии 2.0 введена поддержка типов данных, допускающих значения NULL, что дает программистам возможность расширить тип значения, чтобы могли быть представлены все значения базового типа.Эти типы среды CLR, допускающие значения NULL, представляют экземпляр структуры Nullable.Такая возможность особенно полезна, если для типов значений могут устанавливаться и сниматься флажки, что обеспечивает улучшенную совместимость с типами объектов.Типы среды CLR, допускающие значения NULL, не предназначены для хранения значений NULL базы данных, т. к. поведение значения NULL для ANSI SQL отличается от поведения ссылки null (или Nothing в Visual Basic).Для работы со значениями NULL ANSI SQL базы данных используйте значения NULL System.Data.SqlTypes, а не Nullable.Дополнительные сведения о работе в Visual Basic с типами CLR, допускающими значения NULL, см. в разделе Типы значения, допускающие Null (Visual Basic), а для C# см. раздел Использование допускающих значение NULL типов (Руководство по программированию на C#). |
Значения NULL и тройственная логика
Разрешение использовать значения NULL в определениях столбцов вводит в приложение тройственную логику. Результатом оценки сравнения может быть одно из трех условий.
True
False
UNKNOWN
Так как значения NULL рассматриваются как неизвестные, два значения NULL, сравниваемые друг с другом, не считаются равными. В выражениях, использующих арифметические операторы, если какие-либо из операндов равны NULL, результат также равен NULL.
Значения NULL и SqlBoolean
Сравнение каких-либо System.Data.SqlTypes возвратит SqlBoolean. Функция IsNull для каждого SqlType возвращает SqlBoolean и может использоваться для проверки значений NULL. Следующие проверки истинности демонстрируют, как операторы AND, OR и NOT функционируют в присутствии значения NULL. (T=true, F=false и U=неизвестно, т.е. NULL).
Основные сведения о параметре ANSI_NULLS
System.Data.SqlTypes предоставляет ту же семантику, что и в случае, когда параметр ANSI_NULLS установлен в SQL Server. Все арифметические операторы (+, -, *, /, %), битовые операции (~, &, |) и большинство функций возвращают NULL, если какие-либо из операндов или аргументов равны NULL, за исключением операндов или аргументов для свойства IsNull.
Стандарт ANSI SQL-92 не поддерживает columnName = NULL в предложении WHERE. В SQL Server параметр ANSI_NULLS контролирует как допустимость значений NULL по умолчанию, так и оценку сравнений со значениями NULL. Если параметр ANSI_NULLS установлен в значение ON (по умолчанию), в выражениях при проверке на значения NULL должен использоваться оператор IS NULL. Например, при параметре ANSI_NULLS, установленном в значение ON, результатом следующего сравнения всегда является UNKNOWN:
colname > NULL
Результатом сравнения переменной, содержащей значение NULL, также является UNKNOWN:
colname > @MyVariable
Для проверки на значение NULL используется предикат IS NULL или IS NOT NULL. Это может усложнить предложение WHERE. Например, в столбце TerritoryID таблицы «Заказчики AdventureWorks» разрешены значения 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 установлен в OFF, можно создать выражения, использующие оператор равенства для сравнения со значением NULL. Однако нельзя предотвратить в разных соединениях установку параметров NULL для данного соединения. Использование IS NULL для проверки на значения NULL всегда работает, независимо от установки ANSI_NULLS для соединения.
Установка ANSI_NULLS в OFF не поддерживается в DataSet, который всегда следует стандарту ANSI SQL-92 для обработки значений NULL в System.Data.SqlTypes.
Присвоение значений Null
Значения NULL являются особыми, и их семантика хранения и присвоения различается для разных систем типов и систем хранения. Dataset предназначен для использования с различными системами типов и хранения.
В этом разделе описывается семантика присвоения значений NULL для DataColumn в DataRow для разных систем типов.
DBNull.Value
Это присвоение действительно для DataColumn любого типа. Если тип реализует INullable, DBNull.Value приводится в соответствующее строго типизированное значение NULL.SqlType.Null
Все типы данных System.Data.SqlTypes реализуют INullable. Если строго типизированное значение NULL может быть преобразовано в тип данных столбца с использованием неявных операторов приведения, присвоение должно выполняться. В противном случае возникает исключение недопустимого приведения.null
Если NULL является допустимым значением для типа данных данного DataColumn, оно приводится в соответствующее DbNull.Value или Null, связанное с INullable типом (SqlType.Null)derivedUdt.Null
Для столбцов определяемого пользователем типа значения NULL всегда сохраняются на основании типа, связанного с DataColumn. Рассмотрим случай определяемого пользователем типа, связанного с DataColumn, который не реализует INullable, хотя его подкласс реализует. В этом случае, если присваивается значение NULL со строгой типизацией, которое связано с производным классом, оно хранится как нетипизированное DbNull.Value, т. к. хранение значения NULL всегда согласовано с типом данных DataColumn.
![]() |
---|
Структура Nullable<T> или Nullable в настоящее время в DataSet не поддерживается. |
Присвоение для многих столбцов или строк
DataTable.Add, DataTable.LoadDataRow или другие API, которые принимают ItemArray, которые сопоставлены со строкой, сопоставляют «NULL» значению по умолчанию для DataColumn. Если объект в массиве содержит DbNull.Value или его строго типизированный аналог, применяются те же вышеописанные правила.
Кроме того, следующие правила применяются для экземпляра присваивания DataRow.["columnName"] значений NULL.
Используемое по умолчанию значение default является DbNull.Value для всех столбцов, за исключением строго типизированных нулевых столбцов с допустимыми строго типизированными значениями NULL.
Значения NULL никогда не записываются в XML-файлы во время сериализации (в виде "xsi:nil").
Все отличные от NULL значения, включая значения по умолчанию, при сериализации всегда записываются в XML. Это отличается от семантики XSD и XML, где значение NULL (xsi:nil) является явным, а значение по умолчанию — неявным (если оно не присутствует в XML, проверяющее средство синтаксического анализа может получить его из связанной схемы XSD). Для DataTable справедливо и обратное: значение NULL является неявным, а значение по умолчанию — явным.
Всем отсутствующим значениям столбцов для строк, считываемым из входных XML-данных, присваивается значение NULL. Строкам, созданным методом NewRow или аналогичным методом, присваивается значение по умолчанию для DataColumn.
Метод IsNull возвращает true и для DbNull.Value, и для INullable.Null.
Присвоение значений Null
Аргументом по умолчанию для любого экземпляра System.Data.SqlTypes является значение NULL.
Значения NULL в System.Data.SqlTypes зависят от типа и не могут представляться одним значением, таким как DbNull. Для проверки на значения NULL используется свойство IsNull.
Значения NULL могут присваиваться DataColumn, как показано в следующем примере кода. Значения NULL можно непосредственно присваивать переменным SqlTypes без возникновения исключения.
Пример
В следующем примере кода создается DataTable с двумя столбцами, определенными как SqlInt32 и SqlString. Код добавляет одну строку известных значений, одну строку значений 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 оценивает значения NULL в System.Data.SqlTypes, и способом его работы с типами среды CLR. Все методы System.Data.SqlTypes Equals используют семантику базы данных для оценки значений NULL: если одно из значений или оба значения равны NULL, то результатом сравнения является NULL. С другой стороны, результатом использования метода среды CLR Equals на двух System.Data.SqlTypes является true, если оба значения являются NULL. Это отражает разницу между использованием метода экземпляра, например метода среды CLR String.Equals, и использованием статического или общего метода SqlString.Equals.
Следующий пример демонстрирует разницу в результатах методов SqlString.Equals и String.Equals, если каждому посылается пара значений NULL, а затем пара пустых строк.
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