Ответ
Split Query (Разделённый запрос) — это стратегия загрузки связанных данных в Entity Framework Core, при которой один сложный SQL-запрос с несколькими JOIN разбивается на несколько отдельных, более простых запросов. Это решение проблемы "раздувания Cartesian product" (Cartesian Explosion).
Проблема, которую решает Split Query:
При использовании Include() и ThenInclude() для загрузки нескольких коллекций EF Core генерирует один запрос с LEFT JOIN. Если у родительской сущности много дочерних записей в каждой коллекции, результирующий набор данных дублируется для каждой комбинации, что приводит к передаче избыточного объема данных по сети и повышенной нагрузке на память.
Пример: Загрузка заказов (Order) с их позициями (OrderItems).
Стандартный запрос (Single Query):
var orders = context.Orders
.Include(o => o.OrderItems)
.ToList();
Генерирует SQL с LEFT JOIN, что может привести к дублированию данных строк заказа для каждой позиции.
Запрос с использованием Split Query:
var orders = context.Orders
.Include(o => o.OrderItems)
.AsSplitQuery() // Ключевой метод
.ToList();
EF Core сгенерирует два запроса:
SELECT * FROM OrdersSELECT * FROM OrderItems WHERE OrderId IN (...)(для ID заказов, полученных в первом запросе).
Преимущества:
- Уменьшение объёма передаваемых данных по сети, так как исключается дублирование столбцов родительской сущности.
- Повышение производительности для запросов, включающих несколько коллекций с большим количеством данных.
Недостатки и ограничения:
- Дополнительные round-trips к базе данных. Вместо одного запроса выполняется несколько (N+1 для вложенных коллекций, но EF Core оптимизирует это, используя оператор
IN). - Отсутствие согласованности на уровне моментального снимка (snapshot). Если данные изменяются между выполнением разделённых запросов, итоговое состояние загруженного графа объектов может быть несогласованным (риск при высокой параллельной нагрузке).
- Не поддерживается в некоторых сложных сценариях, например, при использовании
Includeс последующей фильтрацией (Where) по коллекции.
Способы настройки:
- Локально для запроса: Использовать метод
AsSplitQuery(). - Глобально для контекста: Настроить в
OnConfiguringметодаDbContext.protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer(connectionString, options => options.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery)); }Выбор между
SingleQuery(по умолчанию) иSplitQueryзависит от конкретной структуры данных и требований к производительности.