在这里,我将添加部分查询,因为它是敏感信息,并且我不会使用真实姓名:
SELECT
`entity`.`id`,
`entity`.`requirements`,
`entity`.`description`,
`entity`.`status`,
`entity_videos`.`length`,
`entity_videos`.`quality`,
`states`.`name`,
`uploads`.`id`,
`uploads`.`name`
FROM `entity`
LEFT JOIN `states` ON `states`.`id` = `entity`.`state_id`
INNER JOIN `uploads` FORCE INDEX (`uploadable_id_index`)
ON `uploads`.`uploadable_type` = 'Entity'
AND `uploads`.`category` = 'Icon'
AND (`uploads`.`uploadable_id` = `entity`.`id`
OR `uploads`.`uploadable_id` = `entity`.`parent_entity_for_icon`
)
INNER JOIN `entity_videos` FORCE INDEX (entity_videos_entity_id_index)
ON `entity_videos`.`entity_id` = `entity`.`id`
WHERE `entity`.`status` = 'active'
问题是 Mysql 优化器不想使用 uploadable_id_index
索引。部分解释:
据我所知,FORCE INDEX
是强制优化器使用索引,除非优化器无法使用索引。我应该怎么做才能强制该索引而不对表进行完整扫描?
我尝试删除 entity_videos_entity_id_index
也尝试将 uploads
表信息添加到 where 子句,但对我没有任何作用。有任何想法吗?非常感谢您的帮助
更新:
在@Barmar和@PaulSpiegel的帮助下,我发现问题出在(uploads.uploadable_id =entity.id OR uploads.uploadable_id=entity.parent_entity_for_icon)
中。经过一段时间的查询,我发现我的情况下最好的解决方案是:
SELECT
`entity`.`id`,
`entity`.`requirements`,
`entity`.`description`,
`entity`.`status`,
`entity_videos`.`length`,
`entity_videos`.`quality`,
`states`.`name`,
`icon`.`id`,
`icon`.`name`,
`parent_offer_icon`.`id`,
`parent_offer_icon`.`name`
FROM `entity`
LEFT JOIN `states` ON `states`.`id` = `entity`.`state_id`
LEFT JOIN `uploads` as `icon` FORCE INDEX (`uploadable_id_index`)
ON `icon`.`uploadable_type` = 'Entity'
AND `icon`.`category` = 'Icon'
AND `icon`.`uploadable_id` = `entity`.`id`
LEFT JOIN `uploads` as `parent_offer_icon` FORCE INDEX (`uploadable_id_index`)
ON `parent_offer_icon`.`uploadable_type` = 'Entity'
AND `parent_offer_icon`.`category` = 'Icon'
AND `parent_offer_icon`.`uploadable_id` = `entity`.`parent_entity_for_icon`
INNER JOIN `entity_videos` FORCE INDEX (entity_videos_entity_id_index)
ON `entity_videos`.`entity_id` = `entity`.`id`
WHERE `entity`.`status` = 'active'
AND (parent_offer_icon.id IS NOT NULL
OR icon.id IS NOT NULL )
我仍然愿意接受其他建议:)
最佳答案
让我们首先将讨厌的 OR
转换为 UNION
:
( SELECT e.id AS eid,
u.id AS uid,
u.name AS uname
FROM `uploads` AS u
INNER JOIN `entity` AS e
ON u.`uploadable_id` = e.`id`
WHERE u.`uploadable_type` = 'Entity'
AND u.`category` = 'Icon'
AND e.status = 'active'
) UNION DISTINCT
( SELECT e.id AS eid,
u.id AS uid,
u.name AS uname
FROM `uploads` AS u
INNER JOIN `entity` AS e
ON u.`uploadable_id` = e.`parent_entity_for_icon`
WHERE u.`uploadable_type` = 'Entity'
AND u.`category` = 'Icon'
AND e.status = 'active'
)
这将需要其中一些索引:
uploads: INDEX(uploadable_type, category, uploadable_id, id, name)
entity: INDEX(parent_entity_for_icon, status, id)
(我假设entity
有PRIMARY KEY(id)
?请提供SHOW CREATE TABLE
,这样我就不必猜测。)如果这么长的索引有问题,请告诉我;我也许可以提供一个解决方法。
通过将OR
转换为UNION
,每个部分可以使用不同的索引。使用OR
时,优化器通常会放弃并做一些低效的事情。
请验证上面的查询是否运行快速并产生合理的输出。然后...
SELECT e.`id`, e.`requirements`, e.`description`, e.`status`,
ev.`length`, ev.`quality`,
s.`name`,
uid, uname
FROM ( the-query-above ) AS i
JOIN `entity` AS e ON e.id = i.eid
LEFT JOIN `states` AS s ON s.`id` = e.`state_id`
INNER JOIN `entity_videos` AS ev ON ev.`entity_id` = i.eid
除了每个表上的PRIMARY KEY(id)
之外,您还需要
entity_videos: INDEX(entity_id) -- good, or
entity_videos: INDEX(entity_id, length, quality) -- better ("covering")
不要使用FORCE INDEX
。
有关索引创建的更多信息:http://mysql.rjweb.org/doc.php/index_cookbook_mysql
关于MySQL 忽略索引,即使使用使用索引或强制索引,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46613873/