我在一个C#应用程序中使用Entity Framework并采用延迟加载。我们发现一个对CPU影响极大的查询,仅计算一个总和。调试Entity Framework生成的查询时,它创建了一个
让我通过一个简化版本的代码来说明它。
我期望生成的查询(耗时0.03秒而不是1.3秒)应该是类似以下的内容。
我怀疑是由于使用了
任何帮助都将不胜感激!
INNER JOIN (SELECT ...
,这不是高效的。当我手动将查询更改为适当的JOIN时,查询时间从1.3秒降至0.03秒。让我通过一个简化版本的代码来说明它。
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秒而不是1.3秒)应该是类似以下的内容。
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; }
}
任何帮助都将不胜感激!
SELECT 1 AS 'X'
并不让我太担心。我认为.DefaultIfEmpty(0)
导致了这种情况,并将该操作推送到DB服务器上。虽然在MySQL中有更好的方法来做到这一点,但我可以理解他们必须在查询生成中采取一些捷径。但缺乏适当的JOIN是没有意义的。 - Jules