MySQL 忽略索引,即使使用使用索引或强制索引

标签 mysql indexing

在这里,我将添加部分查询,因为它是敏感信息,并且我不会使用真实姓名:

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 索引。部分解释:

part of explain querye

据我所知,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)

(我假设entityPRIMARY 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/

相关文章:

design-patterns - 如何从elasticsearch/Kibana获取索引模式配置?

MySQL 索引仅用于最大值和最小值

Mysql删除触发器中未知列错误

mysql - SQL:从历史表中获取最新条目

MySQL 查询结果管道化

python - Django 测试用例警告 : Converting column 'options' from VARCHAR to TEXT

r - 你会如何在 R 中完成这个棘手的子集化?

mysql - 合并包含 1700 万条记录的表

database - 根据数据签名对数据进行分类

php - 将 blob 插入数据库时​​未定义索引 php