有哪些解决方案可以避免 Doctrine2 中聚合计数字段的 select(n+1) 问题?

Posted

技术标签:

【中文标题】有哪些解决方案可以避免 Doctrine2 中聚合计数字段的 select(n+1) 问题?【英文标题】:What are some solutions for avoiding the select(n+1) issue in Doctrine2 for aggregate count fields? 【发布时间】:2014-08-29 01:44:30 【问题描述】:

我有两个实体,用户和地点,它们之间存在多对多关系,以促进用户喜欢某个地点的概念。

作为该功能的一部分,我希望 Place 实体包含一个字段,该字段为我提供收藏它的用户总数。我不需要用户实体本身。

阅读文档,我找到了几个解决方案,但我并不特别喜欢其中任何一个。

聚合字段

使用此解决方案,我只需在 Venue 实体上定义一个整数字段,该字段会随着收藏夹的添加和删除而更新。因此,该值不是即时计算的,而是像任何其他字段一样被检索。

我不喜欢这种方法,因为存在并发问题,并且它使这个概念在代码中管理起来不必要地复杂。

急切加载

使用此方法,我会急切地将关系加载为双向关联,以便 Place 将加载每个收藏它的用户实体作为初始查询过程的一部分。要获得计数,我只需向集合询问其 count()。

这会导致查询次数减少,但检索到的数据量过多,并且随着时间的推移无法很好地扩展。

额外的延迟加载

这是我目前正在使用的。它类似于 Eager Loading 解决方案,因为我确保关系是双向的,并简单地向集合询问它的 count(),但使用额外的惰性获取模式原则足够智能,只发出 COUNT() 查询而不是检索与 Place 实体关联的整个用户列表。

这里的缺点是,如果我加载 N 个 Place 实体,我需要 N+1 个查询,因为每个 Place 都会发出一个单独的 COUNT() 查询。

理想的解决方案

我理想的解决方案是找到一种方法告诉 Doctrine 执行第一个查询以加载集合,然后执行第二个查询以加载集合中 ID 的所有计数,然后填充各自实体中的字段。

我还没有找到一种容易做到这一点的方法。

有没有人有这方面的任何例子,或者是否有其他解决方案可以解决我可能忽略的这个问题?

【问题讨论】:

【参考方案1】:

如果你知道如何用 sql 编写查询,你可以在学说中做到。

请参阅此答案以供参考: Symfony2, Doctrine2 query - select with IF and COUNT

【讨论】:

编写查询与其说是让 Doctrine 争论如何处理数据更重要。我希望它填充一个实体字段。如果可能的话,我想避免临时标量查询和数据处理。唉,看来可能不是。【参考方案2】:

您可以运行类似的 DQL:

SELECT p place, COUNT(u) cnt FROM YourBundle:Place p
                LEFT JOIN p.users u

注意结果数组元素的格式为(每个都是一个数组):

array(
 'place' => your hydrated object (in your case the place),
 'cnt' => your aggregated field (in your case the number of users),
)

【讨论】:

这很令人沮丧,因为我不希望它分开。我希望 Doctrine 了解如何将其填充为实体字段,而不会在查询方面如此昂贵。 你希望学说太聪明了 ;) 无论如何,你理想的解决方案使用两个查询,但我的只使用 1。我能问一下这两个是单独的键有什么问题吗? 问题是我返回要序列化的实体。现在我有一些其他随机值,它不是 someone 必须处理的实体的一部分。因此,要么我现在遍历所有结果并重新组织数据以使其有意义,要么我的客户获得一种笨拙的体验,即 Place 实体的集合不仅仅是 Place 实体的集合,它是数组的集合,其中一个键是place 然后 N 其他键是我不够聪明的字段,无法让 Doctrine 很好地发挥作用。当然不是灾难,但我一直在寻找更好的方法! 我明白了。好吧,我认为您不会找到这样做的“懒惰”方式。我也在寻找这样的解决方案! Custom Hydrators 是可能的解决方案。

以上是关于有哪些解决方案可以避免 Doctrine2 中聚合计数字段的 select(n+1) 问题?的主要内容,如果未能解决你的问题,请参考以下文章

sql 聚合函数都有哪些

SQL Server中的聚合函数都有哪些?

SQL Server中的聚合函数都有哪些?

使用 coalesce() 避免嵌套聚合错误

更新元素的 Doctrine2 缓存

什么时候才值得在Doctrine2中保持反向关系?