Hantera Null-värden
Ett null-värde i en relationsdatabas används när värdet i en kolumn är okänt eller saknas. En null är varken en tom sträng (för datatyper av tecken eller datetime) eller ett nollvärde (för numeriska datatyper). ANSI SQL-92-specifikationen anger att en null måste vara densamma för alla datatyper, så att alla null-värden hanteras konsekvent. Namnområdet System.Data.SqlTypes tillhandahåller null-semantik genom att implementera INullable gränssnittet. Var och en av datatyperna i System.Data.SqlTypes har en egen IsNull
egenskap och ett Null
värde som kan tilldelas till en instans av den datatypen.
Kommentar
.NET Framework version 2.0 introducerade stöd för nullbara värdetyper, vilket gör det möjligt för programmerare att utöka en värdetyp för att representera alla värden av den underliggande typen. Dessa CLR nullable-värdetyper representerar en instans av Nullable strukturen. Den här funktionen är särskilt användbar när värdetyper är boxade och oboxade, vilket ger förbättrad kompatibilitet med objekttyper. CLR nullable value types är inte avsedda för lagring av databas-null eftersom en ANSI SQL-null inte fungerar på samma sätt som en null
referens (eller Nothing
i Visual Basic). Om du vill arbeta med databas-ANSI SQL-nullvärden använder du System.Data.SqlTypes null-värden i stället Nullableför . Mer information om hur du arbetar med CLR-värde nullbara typer i Visual Basic finns i Nullable Value Types ,och för C# se Nullable value types (Nullable value types).
Null- och trevärdeslogik
Om du tillåter null-värden i kolumndefinitioner introduceras logik med tre värden i ditt program. En jämförelse kan utvärderas till något av tre villkor:
Sant
Falsk
Okänt
Eftersom null anses vara okänt anses två nullvärden jämfört med varandra inte vara lika med varandra. Om någon av operanderna är null i uttryck med hjälp av aritmetiska operatorer är resultatet även null.
Null och SqlBoolean
Jämförelse mellan alla System.Data.SqlTypes returnerar en SqlBoolean. Funktionen IsNull
för varje SqlType
returnerar en SqlBoolean och kan användas för att söka efter null-värden. Följande sanningstabeller visar hur operatorerna AND, OR och NOT fungerar i närvaro av ett null-värde. (T=true, F=false och U=unknown eller null.)
Förstå alternativet ANSI_NULLS
System.Data.SqlTypes innehåller samma semantik som när alternativet ANSI_NULLS anges i SQL Server. Alla aritmetiska operatorer (+, -, *, /, %), bitvis operatorer (~, &, |) och de flesta funktioner returnerar null om någon av operanderna eller argumenten är null, förutom egenskapen IsNull
.
STANDARDEN ANSI SQL-92 stöder inte columnName = NULL i en WHERE-sats. I SQL Server styr ANSI_NULLS-alternativet både standard nullabiliteten i databasen och utvärderingen av jämförelser mot nullvärden. Om ANSI_NULLS är aktiverat (standard) måste IS NULL-operatorn användas i uttryck när du testar null-värden. Följande jämförelse ger till exempel alltid okänt resultat när ANSI_NULLS är på:
colname > NULL
Jämförelse med en variabel som innehåller ett null-värde ger också okänt värde:
colname > @MyVariable
Använd predikatet IS NULL eller IS NOT NULL för att testa ett null-värde. Detta kan öka komplexiteten i WHERE-satsen. Till exempel tillåter kolumnen TerritoryID i tabellen AdventureWorks-kund nullvärden. Om en SELECT-instruktion ska testas för null-värden utöver andra måste den innehålla ett IS NULL-predikat:
SELECT CustomerID, AccountNumber, TerritoryID
FROM AdventureWorks.Sales.Customer
WHERE TerritoryID IN (1, 2, 3)
OR TerritoryID IS NULL
Om du ställer in ANSI_NULLS i SQL Server kan du skapa uttryck som använder likhetsoperatorn för att jämföra med null. Du kan dock inte förhindra att olika anslutningar ställer in null-alternativ för den anslutningen. Att använda IS NULL för att testa null-värden fungerar alltid, oavsett ANSI_NULLS inställningar för en anslutning.
Inställningen ANSI_NULLS av stöds inte i en DataSet
, som alltid följer STANDARDEN ANSI SQL-92 för hantering av null-värden i System.Data.SqlTypes.
Tilldela Null-värden
Null-värden är speciella och deras lagrings- och tilldelningssemantik skiljer sig åt mellan olika typsystem och lagringssystem. A Dataset
är utformat för att användas med olika typer och lagringssystem.
I det här avsnittet beskrivs null-semantiken för att tilldela null-värden till en i en DataColumn DataRow i de olika typsystemen.
DBNull.Value
Den här tilldelningen är giltig för en DataColumn
av alla typer. Om typen implementerar INullable
DBNull.Value
, tvingas till lämpligt starkt skrivet Null-värde.
SqlType.Null
Alla System.Data.SqlTypes datatyper implementerar INullable
. Om det starkt inskrivna null-värdet kan konverteras till kolumnens datatyp med implicita cast-operatorer, bör tilldelningen gå igenom. Annars utlöses ett ogiltigt gjutet undantag.
null
Om "null" är ett juridiskt värde för den angivna DataColumn
datatypen tvingas den till lämplig DbNull.Value
eller Null
associerad med INullable
typen (SqlType.Null
)
derivedUdt.Null
För UDT-kolumner lagras null alltid baserat på den typ som är associerad med DataColumn
. Tänk på fallet med en UDT som är associerad med en DataColumn
som inte implementeras INullable
medan dess underklass gör det. I det här fallet, om ett starkt skrivet null-värde som är associerat med den härledda klassen tilldelas, lagras det som ett otypat DbNull.Value
, eftersom null-lagring alltid är konsekvent med DataColumns datatyp.
Kommentar
Strukturen Nullable<T>
eller Nullable stöds inte för närvarande i DataSet
.
Standardvärdet för alla System.Data.SqlTypes instanser är null.
Nullvärden i System.Data.SqlTypes är typspecifika och kan inte representeras av ett enda värde, till exempel DbNull
. Använd egenskapen IsNull
för att söka efter nullvärden.
Null-värden kan tilldelas till en DataColumn enligt följande kodexempel. Du kan tilldela null-värden direkt till SqlTypes
variabler utan att utlösa ett undantag.
Exempel
I följande kodexempel skapas en DataTable med två kolumner definierade som SqlInt32 och SqlString. Koden lägger till en rad med kända värden, en rad med null-värden och itererar sedan genom DataTable, tilldelar värdena till variabler och visar utdata i konsolfönstret.
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
I det här exemplet visas följande resultat:
isColumnNull=False, ID=123, Description=Side Mirror
isColumnNull=True, ID=Null, Description=Null
Tilldelning av flera kolumner (rad)
DataTable.Add
, DataTable.LoadDataRow
, eller andra API:er som accepterar en ItemArray som mappas till en rad, mappar "null" till DataColumns standardvärde. Om ett objekt i matrisen innehåller DbNull.Value
eller dess starkt skrivna motsvarighet tillämpas samma regler som beskrivs ovan.
Dessutom gäller följande regler för en instans av DataRow.["columnName"]
null-tilldelningar:
Standardvärdet är
DbNull.Value
för alla utom de starkt inskrivna null-kolumnerna där det är lämpligt starkt skrivet null-värde.Null-värden skrivs aldrig ut under serialiseringen till XML-filer (som i "xsi:nil").
Alla värden som inte är null, inklusive standardvärden, skrivs alltid ut vid serialisering till XML. Detta är till skillnad från XSD/XML-semantik där ett null-värde (xsi:nil) är explicit och standardvärdet är implicit (om det inte finns i XML kan en validerande parser hämta det från ett associerat XSD-schema). Motsatsen gäller för ett
DataTable
: ett null-värde är implicit och standardvärdet är explicit.Alla saknade kolumnvärden för rader som lästs från XML-indata tilldelas NULL. Rader som skapas med eller NewRow liknande metoder tilldelas DataColumns standardvärde.
Metoden IsNull returnerar
true
för bådeDbNull.Value
ochINullable.Null
.
Jämföra nullvärden med SqlTypes och CLR-typer
När du jämför null-värden är det viktigt att förstå skillnaden mellan hur Equals
metoden utvärderar null-värden i System.Data.SqlTypes jämfört med hur den fungerar med CLR-typer. System.Data.SqlTypesEquals
Alla metoder använder databassemantik för att utvärdera null-värden: om något av eller båda värdena är null ger jämförelsen null. Å andra sidan ger clr-metoden Equals
på två System.Data.SqlTypes sant om båda är null. Detta återspeglar skillnaden mellan att använda en instansmetod, till exempel CLR-metodenString.Equals
, och att använda den statiska/delade metoden . SqlString.Equals
I följande exempel visas skillnaden i resultat mellan SqlString.Equals
metoden och metoden när var och String.Equals
en skickas ett par null-värden och sedan ett par tomma strängar.
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
Koden genererar följande utdata:
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