【问题标题】:Checking for collection is empty generated a ton of additional queries检查集合是空的,产生了大量的额外查询
【发布时间】:2018-04-23 20:44:35
【问题描述】:

以下代码:

示例 1

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

在 14 秒内生成并使用了 345 个数据库查询

示例 2

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

{% endif %}
</td>

在 1.7 秒内生成并使用了 186 个数据库查询。

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

更新:

目录实体:

/**
 * @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; 

【问题讨论】:

  • 另一种方法是将empty($object) 测试移动到控制器中,并且只将非空对象传递给模板。
  • 我认为它仍然会运行相同数量的查询,但它将在控制器中完成。不?我只需要检查集合是否包含任何数据。如果我可以在实体级别甚至查询构建器上使用 findOneBy 而不是 findAll。
  • 您应该使用自定义查询并手动获取附加字段,即使 186 个查询听起来也太多了。

标签: symfony doctrine twig


【解决方案1】:

如果您可以分享您的实体代码和树枝模板代码,那将会很有用。

我感觉在这之前你在某个地方做了一些循环。如果是这样,您将遇到n+1 问题。现在,如果您将关系设置为fetch=EAGER,您可能会获得更好的性能,但不建议这样做。

当您从数据库中获取集合时,您将获得一个PersistentCollection 对象。这是一个未初始化的代理类。但是如果你对它执行任何操作,比如count(),它就会执行一个查询。

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

更新:

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

更多关于here

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

简单的一点是通过各自的实体存储库。 findBy 方法除了一个$criteria 参数外,还接受一个orderBy、一个limit 和一个offset。所以,你可以这样做:

<?php

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

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

使用分页的另一种方式是在QueryBuilder 中。完成构建查询后,您只需调用方法 getQuery 即可。这将返回 DQL 查询。您可以像这样将它传递给教义分页器:

// 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.

这应该表现得更好。

现在,您中的某个人只想返回在它们的关系中有其他项目的项目(即,不是空集合),那么您应该看看教义中更高级的子查询主题,exists()any() 查询生成器上的方法。

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

【讨论】:

  • 这是 twig 模板中唯一导致 sql 查询和页面加载时间大幅飙升的唯一区别。我已经将 fetch=EAGER 添加到了指向配件的关系 listing.catalog 中,但它没有显示出任何大的区别。
  • 那么,您无能为力了。如果您有很多记录,请考虑使用学说的内置分页。此外,请确保将目录和 figment 属性初始化为 ArrayCollection
  • 您将如何在集合中使用分页功能?如果我能做的不多,那么在这样一个简单的场景中,整个对象关系映射就会变得非常低效。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-05
  • 1970-01-01
相关资源
最近更新 更多