Manipulando valores nulos
Um valor nulo em um banco de dados relacional é usado quando o valor em uma coluna é desconhecido ou ausente. Um nulo não é uma cadeia de caracteres vazia (para tipos de dados de caractere ou data/hora) nem um valor zero (para tipos de dados numéricos). A especificação ANSI SQL-92 afirma que um nulo deve ser o mesmo para todos os tipos de dados, para que todos os nulos sejam manipulados de forma consistente. O System.Data.SqlTypes namespace fornece semântica nula implementando a INullable interface. Cada um dos tipos de dados tem System.Data.SqlTypes sua própria IsNull
propriedade e um Null
valor que pode ser atribuído a uma instância desse tipo de dados.
Nota
O .NET Framework versão 2.0 introduziu suporte para tipos de valor anulável, que permitem aos programadores estender um tipo de valor para representar todos os valores do tipo subjacente. Esses tipos de valor anulável CLR representam uma instância da Nullable estrutura. Esse recurso é especialmente útil quando os tipos de valor são encaixotados e desencaixotados, fornecendo compatibilidade aprimorada com tipos de objeto. Os tipos de valor nulo CLR não se destinam ao armazenamento de nulos de banco de dados porque um ANSI SQL null não se comporta da mesma maneira que uma null
referência (ou Nothing
no Visual Basic). Para trabalhar com valores nulos ANSI SQL do banco de dados, use System.Data.SqlTypes nulls em vez de Nullable. Para obter mais informações sobre como trabalhar com tipos anuláveis de valor CLR no Visual Basic, consulte Nullable Value Types, e para C# consulte Nullable value types.
Nulos e lógica de três valores
Permitir valores nulos em definições de coluna introduz lógica de três valores em seu aplicativo. Uma comparação pode avaliar uma de três condições:
True
False
Desconhecido
Como null é considerado desconhecido, dois valores nulos comparados entre si não são considerados iguais. Em expressões que usam operadores aritméticos, se qualquer um dos operandos for nulo, o resultado também será nulo.
Nulos e SqlBoolean
A comparação entre qualquer System.Data.SqlTypes um retornará um SqlBooleanarquivo . A IsNull
função para cada SqlType
retorna a SqlBoolean e pode ser usada para verificar valores nulos. As tabelas de verdade a seguir mostram como os operadores AND, OR e NOT funcionam na presença de um valor nulo. (T=verdadeiro, F=falso e U=desconhecido ou nulo.)
Compreender a opção ANSI_NULLS
System.Data.SqlTypes fornece a mesma semântica de quando a opção ANSI_NULLS está definida no SQL Server. Todos os operadores aritméticos (+, -, *, /, %), operadores bitwise (~, &, |), e a maioria das funções retornam null se qualquer um dos operandos ou argumentos for null, exceto para a propriedade IsNull
.
O padrão ANSI SQL-92 não suporta columnName = NULL em uma cláusula WHERE. No SQL Server, a opção ANSI_NULLS controla a anulabilidade padrão no banco de dados e a avaliação de comparações em relação a valores nulos. Se ANSI_NULLS estiver ativado (o padrão), o operador IS NULL deverá ser usado em expressões ao testar valores nulos. Por exemplo, a comparação a seguir sempre produz desconhecidos quando ANSI_NULLS está ativado:
colname > NULL
A comparação com uma variável que contém um valor nulo também produz resultados desconhecidos:
colname > @MyVariable
Use o predicado IS NULL ou IS NOT NULL para testar um valor nulo. Isso pode aumentar a complexidade da cláusula WHERE. Por exemplo, a coluna TerritoryID na tabela AdventureWorks Customer permite valores nulos. Se uma instrução SELECT for para testar valores nulos além de outros, ela deve incluir um predicado IS NULL:
SELECT CustomerID, AccountNumber, TerritoryID
FROM AdventureWorks.Sales.Customer
WHERE TerritoryID IN (1, 2, 3)
OR TerritoryID IS NULL
Se você definir ANSI_NULLS desativado no SQL Server, poderá criar expressões que usem o operador de igualdade para comparar com null. No entanto, você não pode impedir que conexões diferentes definam opções nulas para essa conexão. Usar IS NULL para testar valores nulos sempre funciona, independentemente das configurações de ANSI_NULLS para uma conexão.
Não há suporte para a configuração ANSI_NULLS desativado em um DataSet
, que sempre segue o padrão ANSI SQL-92 para manipular valores nulos no System.Data.SqlTypes.
Atribuindo valores nulos
Os valores nulos são especiais e sua semântica de armazenamento e atribuição diferem entre diferentes tipos de sistemas e sistemas de armazenamento. A Dataset
é projetado para ser usado com diferentes tipos e sistemas de armazenamento.
Esta seção descreve a semântica nula para atribuir valores nulos a a em DataColumn um DataRow entre os diferentes sistemas de tipo.
DBNull.Value
Esta atribuição é válida para qualquer DataColumn
tipo. Se o tipo implementa INullable
, DBNull.Value
é coagido no valor Null fortemente tipado apropriado.
SqlType.Null
Todos os System.Data.SqlTypes tipos de dados implementam INullable
o . Se o valor nulo fortemente tipado puder ser convertido no tipo de dados da coluna usando operadores de conversão implícitos, a atribuição deverá ser realizada. Caso contrário, uma exceção de elenco inválida será lançada.
null
Se «nulo» for um valor legal para o tipo de dados dado DataColumn
, é coagido a ser apropriado DbNull.Value
ou Null
associado ao INullable
tipo (SqlType.Null
)
derivedUdt.Null
Para colunas UDT, os nulos são sempre armazenados com base no tipo associado ao DataColumn
. Considere o caso de um UDT associado a um DataColumn
que não implementa INullable
enquanto sua subclasse o faz. Nesse caso, se um valor nulo fortemente tipado associado à classe derivada for atribuído, ele será armazenado como um não tipado DbNull.Value
, porque o armazenamento nulo é sempre consistente com o tipo de dados DataColumn.
Nota
A Nullable<T>
estrutura ou Nullable não é suportada atualmente no DataSet
.
O valor padrão para qualquer System.Data.SqlTypes instância é null.
Nulos em System.Data.SqlTypes são específicos do tipo e não podem ser representados por um único valor, como DbNull
. Use a IsNull
propriedade para verificar se há nulos.
Valores nulos podem ser atribuídos a um DataColumn como mostrado no exemplo de código a seguir. Você pode atribuir diretamente valores nulos a SqlTypes
variáveis sem acionar uma exceção.
Exemplo
O exemplo de código a seguir cria um DataTable com duas colunas definidas como SqlInt32 e SqlString. O código adiciona uma linha de valores conhecidos, uma linha de valores nulos e, em seguida, itera através do DataTable, atribuindo os valores a variáveis e exibindo a saída na janela do console.
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
Este exemplo exibe os seguintes resultados:
isColumnNull=False, ID=123, Description=Side Mirror
isColumnNull=True, ID=Null, Description=Null
Atribuição de várias colunas (linha)
DataTable.Add
, DataTable.LoadDataRow
, ou outras APIs que aceitam um ItemArray que é mapeado para uma linha, mapeiam 'null' para o valor padrão do DataColumn. Se um objeto na matriz contiver DbNull.Value
ou sua contraparte fortemente tipada, as mesmas regras descritas acima serão aplicadas.
Além disso, as seguintes regras se aplicam a uma instância de DataRow.["columnName"]
atribuições nulas:
O valor padrão é
DbNull.Value
para todos, exceto as colunas nulas fortemente tipadas, onde é o valor nulo fortemente tipado apropriado.Valores nulos nunca são gravados durante a serialização em arquivos XML (como em "xsi:nil").
Todos os valores não nulos, incluindo padrões, são sempre gravados durante a serialização em XML. Isso é diferente da semântica XSD/XML, onde um valor nulo (xsi:nil) é explícito e o valor padrão está implícito (se não estiver presente em XML, um analisador de validação pode obtê-lo de um esquema XSD associado). O oposto é verdadeiro para um
DataTable
: um valor nulo está implícito e o valor padrão é explícito.Todos os valores de coluna ausentes para linhas lidas da entrada XML são atribuídos NULL. Linhas criadas usando NewRow ou métodos semelhantes recebem o valor padrão de DataColumn.
O IsNull método retorna
true
para ambos eINullable.Null
DbNull.Value
.
Comparando valores nulos com tipos SqlTypes e CLR
Ao comparar valores nulos, é importante entender a diferença entre a maneira como o Equals
método avalia valores nulos em System.Data.SqlTypes comparação com a maneira como ele funciona com tipos CLR. Todos os System.Data.SqlTypesEquals
métodos usam semântica de banco de dados para avaliar valores nulos: se um ou ambos os valores forem nulos, a comparação produzirá null. Por outro lado, usar o método CLR Equals
em dois System.Data.SqlTypes produzirá true se ambos forem nulos. Isso reflete a diferença entre usar um método de instância, como o método CLR String.Equals
, e usar o método estático/compartilhado, SqlString.Equals
.
O exemplo a seguir demonstra a diferença nos resultados entre o SqlString.Equals
método e o String.Equals
método quando cada um é passado um par de valores nulos e, em seguida, um par de cadeias de caracteres vazias.
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
O código produz a seguinte saída:
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