Dela via


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.)

Truth Table

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 INullableDBNull.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:

  1. 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.

  2. Null-värden skrivs aldrig ut under serialiseringen till XML-filer (som i "xsi:nil").

  3. 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.

  4. 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.

  5. Metoden IsNull returnerar true för både DbNull.Value och INullable.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

Se även