Sdílet prostřednictvím


Zpracování hodnot null

Hodnota null v relační databázi se používá, pokud je hodnota ve sloupci neznámá nebo chybí. Hodnota null není prázdný řetězec (pro datové typy character ani datetime) ani nulová hodnota (pro číselné datové typy). Specifikace ANSI SQL-92 uvádí, že hodnota null musí být stejná pro všechny datové typy, aby se všechny hodnoty null zpracovávaly konzistentně. Obor názvů System.Data.SqlTypes poskytuje null sémantiku implementací rozhraní INullable. Každý z datových typů má System.Data.SqlTypes svou vlastní IsNull vlastnost a Null hodnotu, která se dá přiřadit k instanci tohoto datového typu.

Poznámka:

Rozhraní .NET Framework verze 2.0 zavedlo podporu pro typy hodnot s možnou hodnotou null, které programátorům umožňují rozšířit typ hodnoty tak, aby představovaly všechny hodnoty základního typu. Tyto typy hodnot CLR s možnou Nullable hodnotou null představují instanci struktury. Tato funkce je užitečná zejména v případě, že jsou typy hodnot obaleny a rozbaleny, což poskytuje lepší kompatibilitu s typy objektů. Typy hodnot CLR s možností null null nejsou určeny pro ukládání hodnot null databáze, protože hodnota NULL ANSI SQL se nechovají stejně jako null reference (nebo v jazyce Visual Basic). Pro práci s databázovými hodnotami ANSI SQL s hodnotou null použijte System.Data.SqlTypes hodnoty null místo Nullable. Další informace o práci s typy hodnot CLR s možnou hodnotou null v jazyce Visual Basic naleznete v tématu Typy hodnot s možnou hodnotou Null a pro C# viz Typy hodnot s možnou hodnotou Null.

Logika null a Three-Valued

Povolení hodnot null v definicích sloupců zavádí do vaší aplikace tříhodnotovou logiku. Porovnání se může vyhodnotit do jednoho ze tří stavů:

  • Pravdivé

  • Nepravda

  • Neznámý

Vzhledem k tomu, že hodnota null je považována za neznámou, nejsou dvě hodnoty null ve srovnání s ostatními považovány za stejné. Ve výrazech používajících aritmetické operátory, pokud některý z operandů má hodnotu null, je výsledek také null.

Hodnoty typu Null a SqlBoolean

Porovnání mezi všemi System.Data.SqlTypes vrátí hodnotu SqlBoolean. Funkce IsNull pro každou SqlType vrací hodnotu SqlBoolean a lze ji použít ke kontrole hodnot null. Následující tabulky pravdy ukazují, jak operátory AND, OR a NOT fungují v přítomnosti hodnoty null. (T=true, F=false a U=unknown nebo null.)

Tabulka pravdy

Vysvětlení možnosti ANSI_NULLS

System.Data.SqlTypes poskytuje stejnou sémantiku jako při nastavení možnosti ANSI_NULLS na SQL Serveru. Všechny aritmetické operátory (+, -, *, /, %), bitové operátory (~, &, |) a většina funkcí vrátí hodnotu null, pokud některý z operandů nebo argumentů má hodnotu null, s výjimkou vlastnosti IsNull.

Standard ANSI SQL-92 nepodporuje columnName = NULL v klauzuli WHERE. V SQL Serveru řídí možnost ANSI_NULLS výchozí hodnotu null v databázi i vyhodnocení porovnání s hodnotami null. Pokud je ANSI_NULLS zapnutá (výchozí), musí být při testování hodnot null použit operátor IS NULL ve výrazech. Například následující porovnání vždy přináší neznámé, pokud je ANSI_NULLS zapnuto:

colname > NULL  

Porovnání s proměnnou obsahující hodnotu null také přináší neznámé hodnoty:

colname > @MyVariable  

K otestování hodnoty null použijte predikát IS NULL nebo IS NOT NULL. To může do klauzule WHERE přidat složitost. Například sloupec TerritoryID v tabulce AdventureWorks Customer umožňuje hodnoty null. Pokud má příkaz SELECT kromě jiných testovat hodnoty null, musí obsahovat predikát IS NULL:

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

Pokud nastavíte ANSI_NULLS v SQL Serveru, můžete vytvořit výrazy, které používají operátor rovnosti k porovnání s hodnotou null. Nemůžete ale zabránit tomu, aby různá připojení nastavily možnosti null pro toto připojení. Použití IS NULL k testování hodnot null vždy funguje bez ohledu na nastavení připojení ANSI_NULLS.

Nastavení ANSI_NULLS na hodnotu vypnuto není podporováno v rozhraní DataSet, které vždy dodržuje standard ANSI SQL-92 pro zpracování null hodnot v System.Data.SqlTypes.

Přiřazení null hodnot

Nullové hodnoty jsou speciální a jejich sémantika ukládání a přiřazování se liší v různých typech a úložných systémech. A Dataset je navržen tak, aby se používal s různými typy a úložnými systémy.

Tato část popisuje nulovou sémantiku pro přiřazení hodnot null k DataColumn v rámci různých typových systémů DataRow.

DBNull.Value
Toto přiřazení je platné pro libovolný DataColumn typ. Pokud se typ implementuje INullable, DBNull.Value je přetěžován na odpovídající hodnotu null silného typu.

SqlType.Null
Všechny System.Data.SqlTypes datové typy implementují INullable. Pokud lze hodnotu null silného typu převést na datový typ sloupce pomocí implicitních operátorů převodu, přiřazení by mělo být úspěšné. V opačném případě je vyvolána výjimka neplatného přetypování.

null
Pokud je hodnota 'null' platnou hodnotou pro daný DataColumn datový typ, je převedena na příslušný DbNull.Value nebo Null přidružený k typu INullable (SqlType.Null).

derivedUdt.Null
U sloupců UDT se hodnoty null vždy ukládají na základě typu přidruženého k sadě DataColumn. Vezměte v úvahu případ UDT přidruženého k DataColumn, který neimplementuje INullable, zatímco jeho podtřída ano. V tomto případě, pokud je přiřazena hodnota null silného typu, která je spojena s odvozenou třídou, je uložena jako netypována DbNull.Value, protože úložiště hodnot null je vždy v souladu s datovým typem DataColumn.

Poznámka:

Struktura Nullable<T> ani Nullable není v současné době podporována v DataSet.

Výchozí hodnota pro libovolnou System.Data.SqlTypes instanci je null.

System.Data.SqlTypes Hodnoty Null jsou specifické pro typ a nelze je reprezentovat jednou hodnotou, například DbNull. IsNull Pomocí vlastnosti zkontrolujte hodnoty null.

Hodnoty Null lze přiřadit k objektu DataColumn , jak je znázorněno v následujícím příkladu kódu. Můžete přímo přiřadit hodnoty (null) proměnným, aniž by byla vyvolána výjimka.

Příklad

Následující příklad kódu vytvoří DataTable se dvěma sloupci definovanými jako SqlInt32 a SqlString. Kód přidá jeden řádek známých hodnot, jeden řádek hodnot null a pak iteruje DataTable, přiřadí hodnoty proměnným a zobrazí výstup v okně konzoly.

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

Tento příklad zobrazí následující výsledky:

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

Přiřazení více sloupců (řádků)

DataTable.Add, DataTable.LoadDataRow, nebo jiná rozhraní API, která přijímají ItemArray mapování na řádek, mapují 'null' na výchozí hodnotu DataColumn. Pokud objekt v poli obsahuje DbNull.Value nebo jeho protějšek silného typu, použijí se stejná pravidla, jak je popsáno výše.

Kromě toho platí následující pravidla pro přiřazování hodnoty null instanci DataRow.["columnName"]:

  1. Výchozí hodnota je DbNull.Value pro všechny kromě sloupců null silného typu, kde se jedná o odpovídající hodnotu null silného typu.

  2. Hodnoty Null se při serializaci nikdy nezapisují do souborů XML (jako v xsi:nil).

  3. Všechny hodnoty, které nejsou null, včetně výchozích hodnot, se při serializaci do XML vždy zapisují. Je to na rozdíl od sémantiky XSD/XML, kde je explicitní hodnota null (xsi:nil) a výchozí hodnota je implicitní (pokud není k dispozici ve formátu XML, může ji validační analyzátor získat z přidruženého schématu XSD). Opak platí pro DataTable: hodnota null je implicitní a výchozí hodnota je explicitní.

  4. Všechny chybějící hodnoty sloupců pro řádky přečtené ze vstupu XML mají přiřazenou hodnotu NULL. Řádkům vytvořeným pomocí NewRow nebo podobným metodám se přiřadí výchozí hodnota DataColumn.

  5. Metoda IsNull vrátí true pro obě DbNull.Value a INullable.Null.

Porovnání hodnot Null s typy SqlTypes a CLR

Při porovnávání hodnot null je důležité pochopit rozdíl mezi tím, jak Equals metoda vyhodnocuje hodnoty System.Data.SqlTypes null ve srovnání se způsobem, jakým funguje s typy CLR. System.Data.SqlTypes Equals Všechny metody používají k vyhodnocení hodnot null sémantiku databáze: pokud je hodnota null nebo obě hodnoty null, porovnání má hodnotu null. Na druhou stranu použití metody CLR Equals na dvou prvcích System.Data.SqlTypes bude vracet true, pokud jsou oba null. To odráží rozdíl mezi použitím metody instance, jako je clr String.Equals metoda, a pomocí statické/sdílené metody, SqlString.Equals.

Následující příklad ukazuje rozdíl ve výsledcích mezi metodou SqlString.Equals a metodou String.Equals při každém předání dvojice hodnot null a pak pár prázdných řetězců.

    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={SqlStringEquals(a, b)}");

        // Compare nulls using instance method String.Equals.
        Console.WriteLine();
        Console.WriteLine("String.Equals instance method:");
        Console.WriteLine($"  Two nulls={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={SqlStringEquals(a, b)}");

        Console.WriteLine();
        Console.WriteLine("String.Equals instance method:");
        Console.WriteLine($"  Two empty strings={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

Kód vytvoří následující výstup:

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

Viz také