Я использую Entity Framework в приложении C# и использую отложенную загрузку. Мы заметили, что один запрос чрезвычайно сильно влияет на наш процессор, который просто вычисляет сумму. При отладке запроса, сгенерированного Entity Framework, он создает INNER JOIN (SELECT ...
, который не является производительным. Когда я вручную изменяю запрос на правильный JOIN, время запроса изменяется от 1,3 сек c до 0,03 сек c.
Позвольте мне проиллюстрировать это упрощенной версией моего кода.
public decimal GetPortfolioValue(Guid portfolioId)
{
var value = DbContext.Portfolios
.Where( x => x.Id.Equals(portfolioId) )
.SelectMany( p => p.Items
.Where( i => i.Status == ItemStatusConstants.Subscribed
&& _activeStatuses.Contains( i.Category.Status ) )
)
.Select( i => i.Amount )
.DefaultIfEmpty(0)
.Sum();
return value;
}
Это генерирует запрос, который выбирает сумму, но выполняет внутреннее соединение в SELECT двух таблиц, соединенных вместе. Я создал pastebin здесь , чтобы сгенерированный запрос не загрязнял этот вопрос, но сокращенная версия будет выглядеть так:
SELECT ...
FROM `portfolios` AS `Extent1`
INNER JOIN (SELECT
`Extent2`.*,
`Extent3`.*
FROM `items` AS `Extent2`
INNER JOIN `categories` AS `Extent3` ON `Extent3`.`id` =
`Extent2`.`category_id`) AS `Join1`
ON `Extent1`.`id` = `Join1`.`portfolio_id`
AND ((`Join1`.`status` = @gp1)
AND (`Join1`.`STATUS1` IN (@gp2, @gp3, @gp4, @gp5, @gp6)))
WHERE ...
Запрос, который я ожидал бы сгенерировать (и который занимает 0,03). se c вместо 1.3 se c) будет выглядеть примерно так:
SELECT ...
FROM `portfolios` AS `Extent1`
INNER JOIN `items` AS `Extent2` ON `Extent2`.`portfolio_id` = `Extent1`.`id`
INNER JOIN `categories` AS `Extent3` ON `Extent3`.`id` = `Extent2`.`category_id`
AND ((`Extent2`.`status` = @gp1)
AND (`Extent3`.`status` IN (@gp2, @gp3, @gp4, @gp5, @gp6)))
WHERE ...
Я подозреваю, что это связано с .SelectMany
, но я не понимаю, как мне следует переписать запрос LINQ, чтобы сделать его более эффективным. Что касается сущностей, свойства связывания являются виртуальными и имеют настроенный внешний ключ:
public class Portfolio
{
public Guid Id { get; set; }
public virtual ICollection<Item> Items { get; set; }
}
public class Item
{
public Guid Id { get; set; }
public Guid PortfolioId { get; set; }
public Guid CategoryId { get; set; }
public decimal Amount { get; set; }
public string Status { get; set; }
public virtual Portfolio Portfolio { get; set; }
public virtual Category Category { get; set; }
}
public class Category
{
public Guid Id { get; set; }
public string Status { get; set; }
public virtual ICollection<Item> Items { get; set; }
}
Любая помощь будет принята с благодарностью!