Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Introduction
Databáze SQL pracují se 3hodnotovou logikou (true, false, null) při porovnávání, a ne s logickou logikou jazyka C#. Při překladu dotazů LINQ do SQL se EF Core pokusí nahradit rozdíl zavedením dalších kontrol null pro některé prvky dotazu.
Pro ilustraci pojďme definovat následující entitu:
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; }
}
a provést některé dotazy:
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);
První dva dotazy vytvářejí jednoduchá porovnání. V prvním dotazu jsou oba sloupce bez hodnoty null, takže nejsou potřeba kontroly null. Ve druhém dotazu NullableInt by mohl obsahovat null, ale Id není povolen hodnotou null; porovnávání null s nenull výsledkem je null, což by bylo filtrováno operací WHERE. Nejsou tedy potřeba žádné další termíny.
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]
Třetí dotaz zavádí kontrolu null. Pokud NullableInt je null, porovnání Id <> NullableInt dává výsledek null, které by bylo vyfiltrováno operací WHERE. Z logické logiky by se ale tento případ měl vrátit jako součást výsledku. Proto EF Core přidá potřebnou kontrolu, aby to zajistil.
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
Dotazy čtyři a pět ukazují vzor, když oba sloupce mají hodnotu null. Stojí za zmínku, že operace <> vytváří složitější (a potenciálně pomalejší) dotaz než operace ==.
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)
Zpracování nulovatelných hodnot ve funkcích
Mnoho funkcí v SQL může vrátit pouze null výsledek, pokud jsou některé z jejich argumentů null. EF Core toho využívá k vytváření efektivnějších dotazů.
Následující dotaz znázorňuje optimalizaci:
var query = context.Entities.Where(e => e.String1.Substring(0, e.String2.Length) == null);
Vygenerovaný SQL je následující (nemusíme vyhodnotit SUBSTRING funkci, protože bude mít hodnotu null pouze v případě, že některý z argumentů má hodnotu null.):
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
Optimalizaci lze také použít pro uživatelem definované funkce. Další podrobnosti najdete na stránce mapování funkcí definovaných uživatelem .
Psaní výkonných dotazů
Porovnání nenulových sloupců je jednodušší a rychlejší než porovnání nulových sloupců. Pokud je to možné, zvažte označení sloupců jako nenulové.
Kontrola rovnosti (
==) je jednodušší a rychlejší než kontrola nerovnosti (!=), protože dotaz nemusí rozlišovat mezinullafalsevýsledkem. Kdykoli je to možné, použijte porovnání rovnosti. Prosté znegující porovnání s==je ve skutečnosti stejné jako!=, takže nezlepší výkon.V některých případech je možné zjednodušit složité porovnání explicitním filtrováním hodnot
nullze sloupce - například když nejsou přítomny žádné hodnotynullnebo tyto hodnoty nejsou relevantní ve výsledku. Představte si následující příklad:
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));
Tyto dotazy vytvářejí následující 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)))
Ve druhém dotazu null se výsledky explicitně odfiltrují ze String1 sloupce. EF Core může během porovnání bezpečně považovat String1 sloupec za nenulový, což vede k jednoduššímu dotazu.
Použití relační sémantiky null hodnot
Je možné zakázat kompenzaci porovnání null a přímo použít relační sémantiku null. To lze provést voláním UseRelationalNulls(true) metody v tvůrci možností uvnitř OnConfiguring metody:
new SqlServerDbContextOptionsBuilder(optionsBuilder).UseRelationalNulls();
Výstraha
Při použití sémantiky s relační hodnotou null už dotazy LINQ nemají stejný význam jako v jazyce C# a můžou přinést jiné výsledky, než se čekalo. Při použití tohoto režimu buďte opatrní.