Doctrine加入两个具有单表继承的模型,第二个连接减慢速度

时间:2014-03-29 20:52:23

标签: php mysql symfony doctrine-orm single-table-inheritance

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

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

所以我开始在查询中添加联接以消除所有额外的查询。它适用于第一个项目类型,主查询运行在101.80毫秒。 (根据Symfony Profiler工具栏)

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

只要我添加第二个联接,查询就会减慢到 24050.14 ms

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

我还没有尝试过第三次加入,担心它会让服务器崩溃。

真正奇怪的是,如果我使用Doctrine查询记录并获得确切的查询,并针对我的数据库手动运行它,它只运行0.2秒。

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

任何建议都将不胜感激。谢谢!

编辑:4/3

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

SQL小提示架构(减去在这种情况下未加载的其他表的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 ms)中的查询

    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;

2 个答案:

答案 0 :(得分:1)

我将以简单的方式解释,我认为您正尝试将名为项目的3个表格加入项目类别项目类别分配所以可能想要这样的东西:

    SELECT item.id, item.name, item.value FROM item
       LEFT JOIN item category
         INNER JOIN item category assignment
       ON item category.id = item category assignment.id
       ON item category.id = item.id

通过中间表项目类别项目加入项目类别分配。由于项目和项目类别之间的连接是LEFT JOIN,因此您将获得所有项目记录。

哪个是替代方案:

         SELECT item.id, item.name, item.value FROM item
         LEFT JOIN item category ON item category.id = item.id
         LEFT JOIN item category assignment ON item category.id = item category assignment.id

答案 1 :(得分:1)

对象和属性与行和列之间的区别在这里并不重要。

您尝试执行一系列LEFT JOIN s,您应该使用UNION s。

请参阅CROSS JOIN和笛卡儿产品的Microsoft's take。他们真的总结得很好。

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

您的意思是:"请帮助我改进我的3模型Doctrine查询,以便在不到550毫秒的时间内运行。" ?