草庐IT

php - Doctrine 用单表继承连接两个模型,第二个连接变慢

coder 2023-10-02 原文

我有 3 个模型使用单表继承。它们适用于可在我们网站上购买的三种不同类型的商品。这些项目被放置在类别中,因此类别模型具有用于映射三种类型中的每一种的属性。

当使用一个简单的选择来获取所有类别,然后显示它们的名称和类别中每种类型项目的数量时,Doctrine 总共在 549 毫秒内执行了 361 次查询。 (一个用于类别列表,然后一个用于类别中的每种类型。)

所以我开始向查询中添加连接以消除所有额外的查询。它对第一个项目类型运行良好,主查询运行时间为 101.80 毫秒。 (根据 Symfony Profiler 工具栏)

$this->_em->createQueryBuilder()
        ->select([$alias, 'courses'])
        ->from($this->_entityName, $alias)
        ->leftJoin("{$alias}.courses", 'courses');

一旦我添加第二个连接,查询就会减慢到 24050.14 毫秒

$qb = $this->_em->createQueryBuilder()
        ->select([$alias, 'courses', 'bundles'])
        ->from($this->_entityName, $alias)
        ->leftJoin("{$alias}.courses", 'courses')
        ->leftJoin("{$alias}.bundles", 'bundles');

我什至还没有尝试过第三次连接,担心它只会让服务器崩溃。

真正奇怪的是,如果我使用 Doctrine Query Logging 并获得准确的查询,然后针对我的数据库手动运行它,它运行仅需 0.2 秒。

该表在所有 FK 列和鉴别器列上都有索引。

如有任何建议,我们将不胜感激。谢谢!

编辑:4/3

我必须让分支回到其他问题的工作点才能回到这个问题。

SQL Fiddle with Schema(减去在这种情况下未加载的其他表的 FK):http://sqlfiddle.com/#!2/85051

所以,我有没有连接的页面,它在 820.38 毫秒内进行了 382 次延迟加载查询。当我手动加入而不是依赖延迟加载时,它对 130 的影响是 21159(这只加入了 2 个模型,所以它仍然延迟加载了第三个)

$qb = $this->_em->createQueryBuilder()
        ->select([$alias, 'courses', 'bundles'])
        ->from($this->_entityName, $alias)
        ->leftJoin("{$alias}.courses", 'courses')
        ->leftJoin("{$alias}.bundles", 'bundles');

这是来自 Symfony 工具栏的查询(20241.26 毫秒)

    SELECT 
  i0_.description AS description0, 
  i0_.id AS id1, 
  i0_.name AS name2, 
  i0_.created_at AS created_at3, 
  i0_.updated_at AS updated_at4, 
  i0_.display_order AS display_order5, 
  i1_.atccode AS atccode6, 
  i1_.version AS version7, 
  i1_.description AS description8, 
  i1_.online_price AS online_price9, 
  i1_.mail_price AS mail_price10, 
  i1_.is_featured AS is_featured11, 
  i1_.code AS code12, 
  i1_.hours AS hours13, 
  i1_.summary AS summary14, 
  i1_.seo_keywords AS seo_keywords15, 
  i1_.seo_description AS seo_description16, 
  i1_.asha_code AS asha_code17, 
  i1_.preview_name AS preview_name18, 
  i1_.preview_link AS preview_link19, 
  i1_.preview_type AS preview_type20, 
  i1_.is_active AS is_active21, 
  i1_.id AS id22, 
  i1_.name AS name23, 
  i1_.created_at AS created_at24, 
  i1_.updated_at AS updated_at25, 
  i1_.deleted_at AS deleted_at26, 
  i1_.goals AS goals27, 
  i1_.disclosure_statement AS disclosure_statement28, 
  i1_.embedded_video AS embedded_video29, 
  i1_.broadcast_chat_link AS broadcast_chat_link30, 
  i2_.atccode AS atccode31, 
  i2_.version AS version32, 
  i2_.description AS description33, 
  i2_.online_price AS online_price34, 
  i2_.mail_price AS mail_price35, 
  i2_.is_featured AS is_featured36, 
  i2_.code AS code37, 
  i2_.hours AS hours38, 
  i2_.summary AS summary39, 
  i2_.seo_keywords AS seo_keywords40, 
  i2_.seo_description AS seo_description41, 
  i2_.asha_code AS asha_code42, 
  i2_.preview_name AS preview_name43, 
  i2_.preview_link AS preview_link44, 
  i2_.preview_type AS preview_type45, 
  i2_.is_active AS is_active46, 
  i2_.id AS id47, 
  i2_.name AS name48, 
  i2_.created_at AS created_at49, 
  i2_.updated_at AS updated_at50, 
  i2_.deleted_at AS deleted_at51, 
  i0_.created_by AS created_by52, 
  i0_.updated_by AS updated_by53, 
  i1_.type AS type54, 
  i1_.item_format_id AS item_format_id55, 
  i1_.company_id AS company_id56, 
  i1_.created_by AS created_by57, 
  i1_.updated_by AS updated_by58, 
  i1_.format_video_id AS format_video_id59, 
  i1_.exam_id AS exam_id60, 
  i1_.survey_id AS survey_id61, 
  i1_.royalty_owner_id AS royalty_owner_id62, 
  i2_.type AS type63, 
  i2_.item_format_id AS item_format_id64, 
  i2_.company_id AS company_id65, 
  i2_.created_by AS created_by66, 
  i2_.updated_by AS updated_by67 
FROM 
  item_category i0_ 
  LEFT JOIN item_category_assignment i3_ ON i0_.id = i3_.item_category_id 
  LEFT JOIN item i1_ ON i1_.id = i3_.item_id 
  AND i1_.type IN ('Product') 
  AND (
    (
      i1_.deleted_at IS NULL 
      OR i1_.deleted_at > '2014-04-03 13:50:45'
    )
  ) 
  LEFT JOIN item_category_assignment i4_ ON i0_.id = i4_.item_category_id 
  LEFT JOIN item i2_ ON i2_.id = i4_.item_id 
  AND i2_.type IN ('Bundle') 
  AND (
    (
      i2_.deleted_at IS NULL 
      OR i2_.deleted_at > '2014-04-03 13:50:45'
    )
  ) 
WHERE 
  i0_.id IN (
    '108', 
    '175', 
    '100', 
    '202', 
    '198', 
    '203', 
    '199', 
    '200', 
    '201', 
    '197', 
    '101', 
    '98', 
    '102', 
    '131', 
    '105', 
    '41', 
    '72', 
    '64', 
    '73', 
    '194', 
    '195', 
    '29', 
    '189', 
    '139', 
    '103', 
    '37', 
    '99', 
    '14', 
    '110', 
    '193', 
    '80', 
    '111', 
    '68', 
    '183', 
    '39', 
    '71', 
    '53', 
    '66', 
    '178', 
    '179', 
    '180', 
    '176', 
    '174', 
    '75', 
    '17', 
    '32', 
    '81', 
    '181', 
    '182', 
    '74', 
    '104', 
    '184', 
    '26', 
    '49', 
    '190', 
    '191', 
    '36', 
    '24', 
    '85', 
    '30', 
    '107', 
    '91', 
    '90', 
    '185', 
    '23', 
    '196', 
    '60', 
    '89', 
    '21', 
    '95', 
    '65', 
    '28', 
    '33', 
    '58', 
    '187', 
    '9', 
    '132', 
    '12', 
    '43', 
    '192', 
    '5', 
    '62', 
    '40', 
    '87', 
    '7', 
    '83', 
    '27', 
    '6', 
    '86', 
    '10', 
    '13', 
    '15', 
    '70', 
    '69', 
    '121', 
    '67', 
    '93', 
    '97', 
    '92', 
    '94', 
    '188', 
    '177', 
    '82', 
    '96', 
    '42', 
    '137', 
    '19', 
    '11', 
    '63', 
    '20', 
    '51', 
    '57', 
    '8', 
    '22', 
    '48', 
    '35', 
    '4', 
    '135', 
    '61', 
    '186', 
    '106', 
    '109', 
    '88', 
    '16', 
    '31', 
    '34'
  ) 
ORDER BY 
  i0_.name ASC

及其解释:

1   SIMPLE       i0_    ALL     PRIMARY                                                                       126   Using where; Using filesort  ,
1   SIMPLE       i3_    ref     item_category_id         item_category_id  4        cems-staging.i0_.id       6                                  ,
1   SIMPLE       i1_    eq_ref  PRIMARY                    PRIMARY           4        cems-staging.i3_.item_id  1     Using where                  ,
1   SIMPLE       i4_    ref     item_category_id         item_category_id  4        cems-staging.i0_.id       6                                  ,
1   SIMPLE       i2_    eq_ref  PRIMARY                    PRIMARY           4        cems-staging.i4_.item_id  1     Using where   

* PHP *

这是 Items 模型上的映射:

 /**
 * @var ArrayCollection
 * @ORM\ManyToMany(targetEntity="models\Category")
 * @ORM\JoinTable(name="item_category_assignment",
 *    joinColumns={@ORM\JoinColumn(name="item_id", referencedColumnName="id")},
 *    inverseJoinColumns={@ORM\JoinColumn(name="item_category_id", referencedColumnName="id")}
 * )
 */
protected $categories;

以及类别模型上的映射

    /**
 * @var ArrayCollection
 *
 * @ORM\ManyToMany(targetEntity="models\Course")
 * @ORM\JoinTable(name="item_category_assignment",
 *    inverseJoinColumns={@ORM\JoinColumn(name="item_id", referencedColumnName="id")},
 *    joinColumns={@ORM\JoinColumn(name="item_category_id", referencedColumnName="id")}
 * )
 */
protected $courses;

/**
 * @var ArrayCollection
 *
 * @ORM\ManyToMany(targetEntity="models\Bundle", mappedBy="categories", orphanRemoval=true)
 * @ORM\JoinTable(name="item_category_assignment",
 *    inverseJoinColumns={@ORM\JoinColumn(name="item_id", referencedColumnName="id")},
 *    joinColumns={@ORM\JoinColumn(name="item_category_id", referencedColumnName="id")}
 * )
 */
protected $bundles;

/**
 * @var ArrayCollection
 *
 * @ORM\ManyToMany(targetEntity="models\Package", mappedBy="categories", orphanRemoval=true)
 * @ORM\JoinTable(name="item_category_assignment",
 *    inverseJoinColumns={@ORM\JoinColumn(name="item_id", referencedColumnName="id")},
 *    joinColumns={@ORM\JoinColumn(name="item_category_id", referencedColumnName="id")}
 * )
 */
protected $packages;

最佳答案

对象和属性与行和列之间的区别在这里真的无关紧要。

您正在尝试执行一系列 LEFT JOIN,您可能应该使用 UNION

请参阅Microsoft's takeCROSS JOIN 和笛卡尔积上。他们真的总结得很好。

编辑:我认为这回答了“为什么我的第二次加入很慢?”的问题。

您的意思是要问:“请帮助我改进我的 3 模型 Doctrine 查询以在不到 550 毫秒内运行。” ?

关于php - Doctrine 用单表继承连接两个模型,第二个连接变慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22736761/

有关php - Doctrine 用单表继承连接两个模型,第二个连接变慢的更多相关文章

  1. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  2. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

  3. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  4. ruby-on-rails - 在混合/模块中覆盖模型的属性访问器 - 2

    我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah

  5. ruby-on-rails - 如何验证非模型(甚至非对象)字段 - 2

    我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss

  6. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

    我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

  7. ruby - 续集在添加关联时访问many_to_many连接表 - 2

    我正在使用Sequel构建一个愿望list系统。我有一个wishlists和itemstable和一个items_wishlists连接表(该名称是续集选择的名称)。items_wishlists表还有一个用于facebookid的额外列(因此我可以存储opengraph操作),这是一个NOTNULL列。我还有Wishlist和Item具有续集many_to_many关联的模型已建立。Wishlist类也有:selectmany_to_many关联的选项设置为select:[:items.*,:items_wishlists__facebook_action_id].有没有一种方法可以

  8. ruby-on-rails - 如何将验证与模型分开 - 2

    我有一些非常大的模型,我必须将它们迁移到最新版本的Rails。这些模型有相当多的验证(User有大约50个验证)。是否可以将所有这些验证移动到另一个文件中?说app/models/validations/user_validations.rb。如果可以,有人可以提供示例吗? 最佳答案 您可以为此使用关注点:#app/models/validations/user_validations.rbrequire'active_support/concern'moduleUserValidationsextendActiveSupport:

  9. ruby - 无法在 60 秒内获得稳定的 Firefox 连接 (127.0.0.1 :7055) - 2

    我使用的是Firefox版本36.0.1和Selenium-Webdrivergem版本2.45.0。我能够创建Firefox实例,但无法使用脚本继续进行进一步的操作无法在60秒内获得稳定的Firefox连接(127.0.0.1:7055)错误。有人能帮帮我吗? 最佳答案 我遇到了同样的问题。降级到firefoxv33后一切正常。您可以找到旧版本here 关于ruby-无法在60秒内获得稳定的Firefox连接(127.0.0.1:7055),我们在StackOverflow上找到一个类

  10. ruby-on-rails - Rails 模型——非持久类成员或属性? - 2

    对于Rails模型,是否可以/建议让一个类的成员不持久保存到数据库中?我想将用户最后选择的类型存储在session变量中。由于我无法从我的模型中设置session变量,我想将值存储在一个“虚拟”类成员中,该成员只是将值传递回Controller。你能有这样的类(class)成员吗? 最佳答案 将非持久属性添加到Rails模型就像任何其他Ruby类一样:classUser扩展解释:在Ruby中,所有实例变量都是私有(private)的,不需要在赋值前定义。attr_accessor创建一个setter和getter方法:classUs

随机推荐