检查收集是否为空会产生大量额外查询

时间:2018-04-23 20:44:36

标签: symfony doctrine twig

以下代码:

示例1

<td>
{% if listing.catalog is not empty %}
   {% if listing.catalog.fitments is not empty %}
   Y
   {% else %}
   N
   {% endif %}
{% endif %}
</td>

在14s生成并使用345个DB查询

示例2

<td>
{% if listing.catalog is not empty %}

{% endif %}
</td>

在1.7s生成并使用186个DB查询。

我知道,一旦我触摸它,学说会尝试提取所有收集数据,但这非常昂贵。有没有更有效的方法来检查是否存在至少一个集合?

更新:

目录实体:

/**
 * @ORM\OneToMany(targetEntity="Fitment", mappedBy="catalog", orphanRemoval=true, cascade={"persist"}, fetch="EAGER")
 * @ORM\OrderBy({"createTime" = "DESC"})
 */

private $fitments;

/**
 * Get fitments.
 *
 * @return \Doctrine\Common\Collections\Collection
 */
public function getFitments() {
    return $this->fitments;
}

装修实体:

/**
 * @ORM\ManyToOne(targetEntity="Catalog", inversedBy="fitments")
 * @ORM\JoinColumn(name="catalog_id", referencedColumnName="catalog_id")
 */
private $catalog; 

1 个答案:

答案 0 :(得分:1)

如果您可以分享您的实体代码和twig模板代码,那将非常有用。

我觉得在这之前你正在做某些循环。如果是这样,您将遇到n+1问题。现在,如果您将关系设置为fetch=EAGER,则可能会有更好的效果,但不建议这样做。

从db获取集合时,会得到一个PersistentCollection对象。它是一个未初始化的代理类。但是,如果您对其执行任何操作,例如count(),它将执行查询。

如果你有一个循环,我会考虑重构一些代码。问问自己是否真的有必要做这个循环或想出另一种方法来解决你的问题。也许是时候向存储库添加一些QueryBuilder魔法,或者如果你正在处理一个用于显示数据的表格,那么可以使用doctrine的内置分页。

更新:

首先,ORM并不是为了帮助您轻松创建查询并解决您的设计问题。它们代表对象关系映射器。它们的目的是将数据库表和关系抽象为对象,这样您就可以以更面向对象的方式处理业务逻辑(并获得自动完成的好处),而不是使用数组或stdClasses。当然,它们包括一些开箱即用的基本操作,但这不是它们存在的原因,而是一个附带的好处。作为开发人员,您需要了解自己的工具并分析使用它们的最佳方法。

有关here

的更多信息

话虽如此,在教条中使用分页有两种方式:一种简单的方式,一种更为复杂的方式。

简单的位是通过相应的实体存储库。除了findBy参数外,$criteria方法还需要orderBylimitoffset。所以,你可以这样做:

<?php

$repository->findBy([], null, $request->query->getInt('limit', 10), $request->query->getInt('offset', 0));

当您执行请求时,这将为您提供前10个结果。如果要修改数据库切片的大小,可以在请求的查询参数中使用限制和偏移量。

使用分页的另一种方式是QueryBuilder。完成构建查询后,只需调用方法getQuery即可。这将返回DQL查询。您可以将其传递给doctrine paginator,如下所示:

// Pass the first result and max result from the request object, not like I'm doing here.
$query = $this->createQueryBuilder('l');
$query->innerJoin('l.catalog', 'c')
    ->innerJoin('c.fitments', 'f')
    ->addSelect('c')
    ->addSelect('f');
$dqlQuery = $query->where('some = condition')->setFirstResult(0)->setMaxResults(10)->getQuery();

$paginator = new Paginator($dqlQuery, true);

然后,如果你遍历那个分页器,你将拥有这些项目,还有一些关于你总共有多少记录和每页记录的好信息,这样你就可以构建一个有效的分页系统。更多关于here

这应该更好。

现在,你想要只返回其关系中包含其他项目的项目(即非空集合),那么你应该看看{{1}中更深层的主题中的子查询主题。查询构建器上的}或exists()方法。

旁注:当您在任何类型的应用程序中返回集合时,分页是必须的。您永远不想立即返回整个结果集,原因很明显 - 当您的结果集增长到10.000条记录时会发生什么?您可以轻松地耗尽内存限制。如果您正在创建一个表,正如我所看到您正在为您发布的一些代码做的那样,该表必须有行限制。