我有3个模型使用单表继承。它们适用于三种不同类型的物品,可以在我们的网站上购买。项目被放入类别中,因此Category模型具有映射三种类型中的每一种的属性。
当使用一个简单的选择来获取所有类别,然后显示它们的名称和类别中每种类型的项目的数量时,Doctrine总共在549毫秒内执行361次查询。(一个用于类别列表,另一个用于类别内的每种类型)
所以我开始在查询中添加连接,以消除所有额外的查询。对于第一个项目类型,它工作得很好,主查询运行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');
我甚至还没有尝试过第三次连接,担心它会使服务器崩溃。
真正奇怪的是,如果我使用学说查询日志并获得确切的查询,并手动运行它对我的数据库,它运行在只有0.2秒。
表在所有FK列和discriminator列上都有索引。
如有任何建议,我将不胜感激。谢谢!编辑: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 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
AND the explain for it:
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
s,你可能应该使用UNION
s。
请参阅微软对CROSS JOIN
和笛卡尔产品的看法。他们总结得很好。
编辑:我认为这回答了"为什么我的第二次连接慢?"的问题。
你的意思是问:"请帮我改进我的3模型Doctrine查询,使其运行时间少于550ms。"div ?
我将用简单的术语来解释它,我认为您正在尝试将3个名为item的表连接到项目类别到项目类别分配所以可能想要这样:
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