【问题标题】:Can I force Doctrine ORM to split up a complicated query?我可以强制 Doctrine ORM 拆分复杂的查询吗?
【发布时间】:2010-11-19 18:11:48
【问题描述】:

我正在使用包含大量连接的 Doctrine 进行查询。有些是 hasOne 关系,有些是 hasMany。

我认为 CakePHP 对每个 hasMany 关系进行单独的查询,但 Doctrine 似乎做了一个巨大的查询。两者都可以水合数据并将一个漂亮的数组返回到您的 php 脚本中,但它们似乎会进行不同的查询集来这样做。使用 Doctrine,只要您的查询包含多个 hasMany 连接,性能就会变得非常糟糕。

使用 CakePHP,默认是拆分查询,但我可以强制它加入 (http://book.cakephp.org/view/872/Joining-tables)。有没有办法在 Doctrine 中做相反的事情:强制它将 hasMany 连接拆分为不同的查询?我已经尝试过文档和 API,但还没有找到任何东西。

【问题讨论】:

  • 如果你把它作为一个物体水合会发生什么?那Doctrine的延迟加载不应该发挥作用吗?
  • 您的问题不是您正在查询的表上的索引不佳吗?如果我是你,我会首先在查询上运行 DESCRIBE 并找出是否有任何索引被使用。
  • @dogmatic69,我想你的意思是解释而不是描述?

标签: php cakephp doctrine


【解决方案1】:

错误的解决方案:如果您的查询是正确的,那么一次查询多个连接应该总是比在多个 SQL 语句中使用分段查询更快。

如果您在添加 n:n 或 1:n 连接后查询速度非常慢,在 Doctrine 中,有几个原因。

多连接查询中最常见的错误之一是使用 LEFT JOIN + WHERE 结构,您可以在其中使用 INNER JOIN 和 ON。考虑这个 DQL 示例:

SELECT a.*, c.*
  FROM Article a
  LEFT JOIN a.Buyer b
  LEFT JOIN b.Creditcard c
  LEFT JOIN c.digitalinfo d
  WHERE b.id = 2 AND d.info LIKE 'TEST%'

如果所有表都有 10000 条记录,这是一个非常慢的查询。它将首先将整个表 b 与表 a 连接起来,产生 10000 ^ 2 行,而在您的 WHERE 子句中,您几乎丢弃了其中的 99.9%。

SELECT a.*, c.*
  FROM Article a
  INNER JOIN a.Buyer b WITH b.id=2
  LEFT JOIN b.Creditcard c
  INNER JOIN c.Digitalinfo d WITH d.info LIKE 'TEST%'

这里,INNER JOIN a.Buyer b 不会以 100 000 000 行结束,但由于它使用扩展的 ON 子句(Doctrine 将此称为 WITH),它只会留下一小部分。因此,与它们在另一个语句中的执行情况相比,其他两个连接将快如闪电。

另外,请确保您始终拥有索引

  • 在您搜索的列上。 (如果您搜索全名,如 FirstName+' '+LastName,请在该序列上创建索引!)
  • 在您执行特定联接的列上
  • 关于外键及其引用设置的字段。

如果您想知道您的数据库在幕后做了什么,例如,您可以在 MySQL 中输入EXPLAIN,然后输入您的查询,它会准确地告诉您为什么需要这么长时间。

【讨论】:

  • 谢谢,我正在使用 LEFT JOIN + WHERE,所以这肯定会有所帮助。不过,性能不佳还有第二个方面,我之前没有解释过。当从具有大量 hasMany 连接的单个查询中返回数据时,数据中存在大量重复。 (即很多行几乎相同)。 PHP 脚本(在本例中为 Doctrine)正在使用大量内存来混合所有这些重复的数据;我希望拆分查询以减少重复并减少内存量。
  • 对此有几点说明。据我所知,与 MySQL 实际执行查询的努力相比,Doctrine Hydration 所花费的时间应该可以忽略不计。您是否分析过您的应用程序? Doctrine 的水合作用是一个不会比以任何其他方式处理大型结果集花费更多时间的过程。你用的是什么补水模式? (试试 HYDRATE_ARRAY,比学说对象快得多。)你也可以使用它附带的 ORM 东西(即找到一个项目,然后导航到它的亲戚,比如$creditcards = Doctrine::getTable('Article')->findOneById($id)->Buyer->Creditcard
【解决方案2】:

Doctrine 允许您连接多个表,遍历多个 oneToMany 关系。这会导致查询的记录数呈指数(表连接数)。 水合过程会细化结果集并从您的二维结果集(表)中创建一棵树。 这对于小的结果集和很少的表连接是合理的。

如果您有相关数量的表和记录,则必须单独查询。

【讨论】:

    【解决方案3】:

    我也遇到过这个问题。我有一个查询需要 0.0060 秒才能以原始形式执行,但阵列水合过程需要 8 秒。由于多个左连接(这是一个具有所有关系的单个实体),查询返回了 2180 行。

    按照@Elvis 的解决方案,我将水合时间降低到 0.3 秒。我所做的只是将查询拆分为两个单独的查询,最终第一个有 60 条记录,其他 30 条记录。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-09
      • 2012-02-02
      • 2015-08-19
      • 2010-12-18
      • 2021-05-27
      • 2015-12-06
      相关资源
      最近更新 更多