原则2 懒惰与渴望,或者为什么它会进行多个查询


Doctrine2 Lazy vs Eager, or why it makes multiple queries

所以我在我的一些项目中使用 QueryBuilder,但其他项目我需要创建 RAW SQL 查询以提高性能,因为我有超过一百万行与它们的关系......

我发现 QueryBuilder 的糟糕之处在于,当您有关系时,它会创建多个查询,例如,我有一个从ProductImageOneToMany关系,并且在反转端有一个ManyToOne

我的查询具有分页,因此仅限于LIMIT 10 OFFSET 0和更多。我的Image实体大约有 270 万行,这就是为什么我使用分页,执行这个简单的查询,它获取Image iProduct p,因为我需要p.title我最终为我的 1 张图像提供 1 个查询,Image的每个Product有 10 个查询。

这是不需要的,只需 2 个查询即可完成,一个用于Image,一个用于Product,使用fetch="EAGER"这就是我得到的。但是我需要fetch="EXTRA_LAZY"放在Product映射中,否则我将再次收到 11 个查询。

只有 10 张图像并不难,但是当用户过滤 500 张图像时,响应时间会越来越长......这就是为什么我最终做了 RAW 查询,最佳性能,没有额外的查询(只有 1 个包含所有内容的查询),但无法像 QueryBuilder 那样处理对象,无法访问 twig 内部image.product.title以获取标题,相反,我需要做SELECT p.title AS product_title并调用image.product_title

所以我需要知道为什么 QueryBuilder 在阅读时如此糟糕,但在持久化对象时却如此出色(简单、快速、干净......),以及我如何使用 QueryBuilder 处理大型数据库而不会降低性能,也不会获得大量额外的不需要的查询。

一个示例查询,是这个

$qb = $this->createQueryBuilder('i');
$qb->innerJoin('i.product', 'p');
$qb->where('i.X = Y');
return $qb->getQuery()->getResult()
使用

$qb->select('i, p'); 似乎只使用一个查询,它是可运行的 raw 有一个 INNER JOIN(这实际上是它在没有 $qb->select() 的情况下工作的方式),但性能仍然低于执行 RAW SQL 查询...对于 10.000 行查询,RAW SQL = 500MS,使用 QB 为 1,100 MS。我知道我不会在使用 10.000 行,但有机会......

问题仍然是一样的,除了对象操作之外,还有什么优点和缺点,RAW SQL已经消失了。以及何时使用LAZY或EAGER 以及为什么,或者为什么/何时不需要它们。

所有这些都可能一劳永逸地结束我的开发团队中的讨论。因为我是QB爱好者。

你有没有做过这样的事情:

SELECT i FROM AcmeBundle:Image i JOIN i.product p WHERE ...

这可以解释许多查询,因为Doctrine不会保留获取的数据。

做这样的事情告诉Doctrine实际保留ImageProduct的获取数据:

SELECT i, p FROM AcmeBundle:Image i JOIN i.product p WHERE ...

然后,您将不需要EAGER也不需要EAGER_LAZY.

我可能错过了你问题的重点。如果我有,请纠正我,我也许可以提出其他建议。

编辑:

$qb = $this->createQueryBuilder('i');
$qb->innerJoin('i.product', 'p');
$qb->addSelect('p'); // Very importang, hints Doctrine to preserve fetched Product
$qb->where('i.X = Y');
return $qb->getQuery()->getResult()

或使用PARTIAL

$qb = $this->createQueryBuilder('i');
$qb->innerJoin('i.product', 'p');
$qb->select('PARTIAL i.{image_field1, image_field2}', 'PARTIAL p.{product_field1, product_field2}'); // Very importang, hints Doctrine to preserve fetched Product
$qb->where('i.X = Y');
return $qb->getQuery()->getResult()