子查询处理的行数多于必要的

时间:2016-07-19 12:00:55

标签: mysql indexing subquery explain

我正在优化我的查询,并发现了一些我无法理解的问题。

我使用以下查询来选择一组类别,将它们与包含类别的旧别名和新别名的表中的别名组合在一起:

SELECT `c`.`id` AS `category.id`, 
    (SELECT `alias`
    FROM `aliases`
    WHERE category_id = c.id
    AND `old` = 0
    AND `lang_id` = 1
    ORDER BY `id` DESC
    LIMIT 1) AS `category.alias`
FROM (`categories` AS c)
WHERE `c`.`status` =  1 AND `c`.`parent_id` =  '11';

parent_id只有2个类别值为11,因此它应该从别名表中查找2个类别。

enter image description here

如果我使用EXPLAIN,它仍然要处理48行。别名表每个类别也包含1个条目(在这种情况下,它可以更多)。所有内容都被编入索引,如果我理解正确,那么它应该立即找到正确的别名。

enter image description here

现在这里很奇怪。当我不按条件中的类别比较别名时,但是通过查询返回的类别ID手动比较,它只会处理1行,与索引一样。

所以我将WHERE category_id = c.id替换为WHERE category_id IN (37, 43),查询变得更快:

enter image description here

我唯一能想到的是子查询不是在查询结果上运行,而是在进行一些过滤之前。欢迎任何形式的解释或帮助!

编辑:愚蠢的我,WHERE IN无法正常工作,因为它没有做出独特的选择。问题仍然存在!

创建表架构

CREATE TABLE `aliases` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
    `lang_id` int(2) unsigned NOT NULL DEFAULT '1',
    `alias` varchar(255) DEFAULT NULL,
    `product_id` int(10) unsigned DEFAULT NULL,
    `category_id` int(10) unsigned DEFAULT NULL,
    `brand_id` int(10) unsigned DEFAULT NULL,
    `page_id` int(10) unsigned DEFAULT NULL,
    `campaign_id` int(10) unsigned DEFAULT NULL,
    `old` tinyint(1) unsigned DEFAULT '0',
    PRIMARY KEY (`id`),
    KEY `product_id` (`product_id`),
    KEY `category_id` (`category_id`),
    KEY `page_id` (`page_id`),
    KEY `alias_product_id` (`product_id`,`alias`),
    KEY `alias_category_id` (`category_id`,`alias`),
    KEY `alias_page_id` (`page_id`,`alias`),
    KEY `alias_brand_id` (`brand_id`,`alias`),
    KEY `alias_product_id_old` (`alias`,`product_id`,`old`),
    KEY `alias_category_id_old` (`alias`,`category_id`,`old`),
    KEY `alias_brand_id_old` (`alias`,`brand_id`,`old`),
    KEY `alias_page_id_old` (`alias`,`page_id`,`old`),
    KEY `lang_brand_old` (`lang_id`,`brand_id`,`old`),
    KEY `id_category_id_lang_id_old` (`lang_id`,`old`,`id`,`category_id`)
) ENGINE=InnoDB AUTO_INCREMENT=112392 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;

1 个答案:

答案 0 :(得分:1)

SELECT ...
    WHERE x=1 AND y=2
    ORDER BY id DESC
    LIMIT 1

将以多种方式之一进行。

由于您没有向我们展示您拥有的索引(SHOW CREATE TABLE),我将介绍一些可能的案例......

  • INDEX(x, y, id) - 这可以找到该条件的最后一行,因此不需要查看多行。
  • 其他一些索引,或者没有索引:从最后id扫描DESCending检查x=1 AND y=2的每一行,在找到这样的行时停止。
  • 其他索引或无索引:扫描整个表格,检查x=1 AND y=2的每一行;将它们收集到临时表中;按id排序;送出一排。

一些EXPLAIN线索:

  • 使用where - 不多说
  • 使用filesort - 它做了一个排序,显然是ORDER BY。 (它可能完全在RAM中完成;忽略'文件'。)
  • 使用索引条件(不是"使用索引") - 这表示内部优化,它可以比旧版本更有效地检查WHERE子句。

信任" Rows"在EXPLAIN。它们通常是合理正确的,但有时候它们的数量级是相同的。这是一个更好的方式来查看"工作量"正在以相当快的速度完成查询:

FLUSH STATUS;
SELECT ...;
SHOW SESSION STATUS LIKE 'Handler%';

使用CREATE TABLE,我可能就如何改进索引提出建议。