Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Einführung
SQL-Datenbanken werden bei Vergleichen auf 3-Wert-Logik (true, false, null) ausgeführt, im Gegensatz zur booleschen Logik von C#. Beim Übersetzen von LINQ-Abfragen in SQL versucht EF Core, den Unterschied auszugleichen, indem zusätzliche NULL-Prüfungen für einige Elemente der Abfrage eingeführt werden.
Um dies zu veranschaulichen, definieren wir die folgende Entität:
public class NullSemanticsEntity
{
public int Id { get; set; }
public int Int { get; set; }
public int? NullableInt { get; set; }
public string String1 { get; set; }
public string String2 { get; set; }
}
und mehrere Abfragen stellen:
var query1 = context.Entities.Where(e => e.Id == e.Int);
var query2 = context.Entities.Where(e => e.Id == e.NullableInt);
var query3 = context.Entities.Where(e => e.Id != e.NullableInt);
var query4 = context.Entities.Where(e => e.String1 == e.String2);
var query5 = context.Entities.Where(e => e.String1 != e.String2);
Die ersten beiden Abfragen erzeugen einfache Vergleiche. In der ersten Abfrage sind beide Spalten nicht nullfähig, sodass keine NULL-Überprüfungen erforderlich sind. In der zweiten Abfrage könnte NullableIntnull enthalten, aber Id ist nicht nullfähig; der Vergleich von null mit einem Nicht-Null-Wert ergibt null, welches durch die WHERE-Operation herausgefiltert wird. Daher sind auch keine zusätzlichen Begriffe erforderlich.
SELECT [e].[Id], [e].[Int], [e].[NullableInt], [e].[String1], [e].[String2]
FROM [Entities] AS [e]
WHERE [e].[Id] = [e].[Int]
SELECT [e].[Id], [e].[Int], [e].[NullableInt], [e].[String1], [e].[String2]
FROM [Entities] AS [e]
WHERE [e].[Id] = [e].[NullableInt]
Die dritte Abfrage führt eine NULL-Prüfung ein.
NullableInt ist null das Ergebnis des Vergleichs Id <> NullableIntnull, das durch den WHERE-Vorgang herausgefiltert wird. Aus boolescher Logikperspektive sollte dieser Fall jedoch als Teil des Ergebnisses zurückgegeben werden. Daher fügt EF Core die notwendige Prüfung hinzu, um sicherzustellen, dass dies gewährleistet ist.
SELECT [e].[Id], [e].[Int], [e].[NullableInt], [e].[String1], [e].[String2]
FROM [Entities] AS [e]
WHERE ([e].[Id] <> [e].[NullableInt]) OR [e].[NullableInt] IS NULL
Abfragen vier und fünf zeigen das Muster an, wenn beide Spalten nullwertebar sind. Es ist erwähnenswert, dass der <> Vorgang komplizierter (und potenziell langsamer) Abfrage als der == Vorgang erzeugt.
SELECT [e].[Id], [e].[Int], [e].[NullableInt], [e].[String1], [e].[String2]
FROM [Entities] AS [e]
WHERE ([e].[String1] = [e].[String2]) OR ([e].[String1] IS NULL AND [e].[String2] IS NULL)
SELECT [e].[Id], [e].[Int], [e].[NullableInt], [e].[String1], [e].[String2]
FROM [Entities] AS [e]
WHERE (([e].[String1] <> [e].[String2]) OR ([e].[String1] IS NULL OR [e].[String2] IS NULL)) AND ([e].[String1] IS NOT NULL OR [e].[String2] IS NOT NULL)
Behandlung von nullablen Werten in Funktionen
Viele Funktionen in SQL können nur dann ein null Ergebnis zurückgeben, wenn einige ihrer Argumente vorhanden sind null. EF Core nutzt dies, um effizientere Abfragen zu erstellen.
Die folgende Abfrage veranschaulicht die Optimierung:
var query = context.Entities.Where(e => e.String1.Substring(0, e.String2.Length) == null);
Die generierte SQL-Anweisung lautet wie folgt (wir müssen die Funktion SUBSTRING nicht auswerten, da sie nur null ist, wenn eines der Argumente null ist.)
SELECT [e].[Id], [e].[Int], [e].[NullableInt], [e].[String1], [e].[String2]
FROM [Entities] AS [e]
WHERE [e].[String1] IS NULL OR [e].[String2] IS NULL
Die Optimierung kann auch für benutzerdefinierte Funktionen verwendet werden. Weitere Details finden Sie auf der Seite zur benutzerdefinierten Funktionszuordnung .
Schreiben von performanten Abfragen
Das Vergleichen nicht nullabler Spalten ist einfacher und schneller als das Vergleichen von nullablen Spalten. Erwägen Sie, Spalten nach Möglichkeit als nicht nullwertig zu markieren.
Die Überprüfung auf Gleichheit (
==) ist einfacher und schneller als die Überprüfung auf Nichtgleichheit (!=), da die Abfrage nicht zwischennullundfalseErgebnis unterscheiden muss. Verwenden Sie den Gleichheitsvergleich nach Möglichkeit. Das einfache Negieren des==Vergleichs ist jedoch effektiv identisch mit!=, sodass es nicht zu Leistungsverbesserungen führt.In einigen Fällen ist es möglich, einen komplexen Vergleich zu vereinfachen, indem Werte aus einer Spalte explizit herausgefiltert
nullwerden , z. B. wenn keinenullWerte vorhanden sind oder diese Werte nicht für das Ergebnis relevant sind. Betrachten Sie das folgenden Beispiel:
var query1 = context.Entities.Where(e => e.String1 != e.String2 || e.String1.Length == e.String2.Length);
var query2 = context.Entities.Where(
e => e.String1 != null && e.String2 != null && (e.String1 != e.String2 || e.String1.Length == e.String2.Length));
Diese Abfragen erzeugen die folgende SQL:These queries produce the following SQL:
SELECT [e].[Id], [e].[Int], [e].[NullableInt], [e].[String1], [e].[String2]
FROM [Entities] AS [e]
WHERE ((([e].[String1] <> [e].[String2]) OR ([e].[String1] IS NULL OR [e].[String2] IS NULL)) AND ([e].[String1] IS NOT NULL OR [e].[String2] IS NOT NULL)) OR ((CAST(LEN([e].[String1]) AS int) = CAST(LEN([e].[String2]) AS int)) OR ([e].[String1] IS NULL AND [e].[String2] IS NULL))
SELECT [e].[Id], [e].[Int], [e].[NullableInt], [e].[String1], [e].[String2]
FROM [Entities] AS [e]
WHERE ([e].[String1] IS NOT NULL AND [e].[String2] IS NOT NULL) AND (([e].[String1] <> [e].[String2]) OR (CAST(LEN([e].[String1]) AS int) = CAST(LEN([e].[String2]) AS int)))
In der zweiten Abfrage null werden Ergebnisse explizit aus der Spalte String1 herausgefiltert. EF Core kann die String1 Spalte beim Vergleich sicher als nicht-nullwertig behandeln, was zu einer einfacheren Abfrage führt.
Verwenden relationaler Nullsemantik
Es ist möglich, die Nullvergleichskompensation zu deaktivieren und relationale Nullsemantik direkt zu verwenden. Dies kann durch den Aufruf der UseRelationalNulls(true) Methode auf dem Options-Builder innerhalb der OnConfiguring Methode erfolgen.
new SqlServerDbContextOptionsBuilder(optionsBuilder).UseRelationalNulls();
Warnung
Wenn Sie relationale Nullsemantik verwenden, haben Ihre LINQ-Abfragen nicht mehr die gleiche Bedeutung wie in C# und können unterschiedliche Ergebnisse als erwartet liefern. Seien Sie vorsichtig, wenn Sie diesen Modus verwenden.