【问题标题】:Doctrine : leftJoin on table generates unnecessary queriesDoctrine : leftJoin on table 会产生不必要的查询
【发布时间】:2010-11-18 17:17:52
【问题描述】:

我的环境的简短描述:

  • Symfony : 1.4.8
  • 教义:1.2.3
  • Centos 5.5 (PHP 5.3.3)
  • MySQL 5.1.52

我在 Symfony 中构建了一个新项目,这是项目架构:

# Car
RjCar:
  actAs: { Timestampable: ~ }
  columns:
    id: { type: integer(4), unsigned: true, primary: true, autoincrement: true }
    year: { type: integer(2), unsigned: true, notnull: true }
    engine_mod: { type: string(1000) }
    exterior_mod: { type: string(1000) }
    suspension_mod: { type: string(1000) }
    audio_mod: { type: string(1000) }
    vote_pos: { type: integer(4), notnull: true, unsigned: true, default: 0 }
    vote_neg: { type: integer(4), notnull: true, unsigned: true, default: 0 }
    views: { type: integer(4), notnull: true, unsigned: true, default: 0 }
    # Foreign keys
    category_id: { type: integer(1), unsigned: true, notnull: true }
    category_check: { type: boolean, notnull: true, default: 0 }
    user_id: { type: integer(5) }
  relations:
    RjCategory: { onDelete: CASCADE, local: category_id, foreign: id, foreignAlias: RjCars }
    sfGuardUser: { onDelete: CASCADE, local: user_id, foreign: id, foreignAlias: RjCars }

# Category
RjCategory:
  columns:
    id: { type: integer(1), unsigned: true, primary: true, autoincrement: true }
    name: { type: string(255), notnull: true}

# I do not include the sfGuardUser schema, but it's the default one from the plugin

当我想用类别名称和用户名检索最后 10 辆汽车时,我在 RjCarTable.class.php 中使用以下代码:

  $last_cars = $this->createQuery('car')
               ->leftJoin('car.sfGuardUser user')
               ->leftJoin('car.RjCategory categ')
               ->orderBy('car.created_at DESC')
               ->limit(10)
               ->execute();

   return $last_cars;

在我的页面上一切看起来都很好,我得到了所有结果,但在调试栏中,我看到了 22 个查询(而不是应该的 2 个)。

这是第一个正常的查询输出:

SELECT
/* Everything about the car */
r.id AS r__id, 
r.year AS r__year, 
r.engine_mod AS r__engine_mod, 
r.exterior_mod AS r__exterior_mod, 
r.suspension_mod AS r__suspension_mod, 
r.audio_mod AS r__audio_mod,  
r.vote_pos AS r__vote_pos, 
r.vote_neg AS r__vote_neg, 
r.views AS r__views, 
r.type_id AS r__type_id, 
r.category_id AS r__category_id, 
r.category_check AS r__category_check, 
r.user_id AS r__user_id, 
r.created_at AS r__created_at, 
r.updated_at AS r__updated_at, 
/* ... hidden because irrelevant... retrieve everything about the sfGuardUser and profile... */
/* Everything about the category */
r2.id AS r2__id, 
r2.name AS r2__name, 
FROM rj_car r 
LEFT JOIN sf_guard_user s ON r.user_id = s.id 
LEFT JOIN rj_category r2 ON r.category_id = r2.id 
ORDER BY r.created_at DESC 
LIMIT 10

所以在那之前一切正常,除了这个查询后面跟着 20 个其他查询来检索有关每个类别的信息(显然每个结果有 2 个查询),而您可以在前面的查询中注意到这些信息是可用的。我不会把它们都放在这里,但这里有一些:

SELECT r.id AS r__id, r.name AS r__name FROM rj_category r WHERE (r.id = '8') LIMIT 1
SELECT r.id AS r__id, r.name AS r__name FROM rj_category r WHERE (r.id = '8') LIMIT 1
SELECT r.id AS r__id, r.name AS r__name FROM rj_category r WHERE (r.id = '9') LIMIT 1
SELECT r.id AS r__id, r.name AS r__name FROM rj_category r WHERE (r.id = '9') LIMIT 1
/* etc.. 20 times */

所以我真正的问题是: - 为什么它执行所有这些不必要的查询,而第一个查询应该有这些信息? - 为什么 sfGuardUser 表没有发生这种情况?我的 RjCar 对象和 sfGuardUser 对象之间的关系显然与 RjCar 和 RjCategory 之间的关系相同。

如果有人已经面临同样的问题,我会很高兴听到它。正如我所说的一切正常,但我更喜欢这个模块不要生成不必要的查询,因为它应该在我的应用程序的主页上执行。

【问题讨论】:

    标签: symfony1 doctrine left-join


    【解决方案1】:

    这种行为看起来很奇怪,绝对不是预期的。可以尝试的一些事情:

    • 您是否正在缓存 Doctrine 查询或结果?有时,在模型更改后查询可能会保持缓存并产生奇怪的效果。
    • 这些查询肯定看起来像 Doctrine 的惰性获取行为。您是否尝试过缩小哪些调用专门触发了这些查询?尝试跟踪哪些特定行触发了查询,看看它们是否有什么特别之处。

    【讨论】:

    • 感谢您的关注。我认为它没有被缓存:我每次都使用教义:build --all --and-load 命令,然后是 cc。如果我从查询中删除 ->leftJoin('RjCategory') 一切都很好。但是感谢延迟加载的提示,我没有考虑过。
    【解决方案2】:

    我打了自己一巴掌:

    这是我在 RjCar.class.php 中找到的内容

    /**
    * Return the category of the car
    */    
    public function getRjCategory(){
      return Doctrine_Core::getTable('RjCategory')->find($this->getCategoryId());
    }
    

    所以它解释了不必要的查询......我什至不记得写过这段代码,但考虑到我是唯一一个在这个项目上工作的人,我猜是我。

    和往常一样,问题出在椅子和键盘之间……

    感谢您的帮助。

    【讨论】:

      【解决方案3】:

      当使用像 Doctrine 和 Hibernate 这样的 ORM 框架时,它经常会产生冗余和丑陋的查询。查询越复杂,在这些系统上就越难看(尝试在您的联接查询中添加“限制”并查看生成的代码)。
      一般来说,框架为 80% 的案例提供了很好的解决方案。对于剩下的 20%,您需要更加努力地工作。通常这是一个公平的权衡。
      如果您担心此查询的输出质量,或者认为您无法承受它导致的延迟 - Doctrine 有一个非常简单的方法来编写和管理自定义查询。不要害怕走那条路……

      【讨论】:

      • 虽然你是对的,有时需要突破 ORM,但这绝对不是其中一种情况。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多