子查询处理比必要更多的行
Subquery processing more rows than necessary
我正在优化我的查询,发现了一些我无法理解的东西。
我正在对 select 一堆类别使用以下查询,将它们与来自 table 的别名组合,其中包含类别的旧别名和新别名:
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
的值为 11 的类别只有 2 个,因此它应该从别名 table 中查找 2 个类别。
仍然,如果我使用 EXPLAIN
,它说它必须处理 48 行。别名 table 每个类别也包含 1 个条目(在这种情况下,它可以更多)。所有内容都已编入索引,如果我理解正确,它应该会立即找到正确的别名。
奇怪的是。当我不按条件的类别比较别名,而是手动按类别 ID 查询 returns 时,它只处理 1 行,如索引所预期的那样。
所以我将 WHERE category_id = c.id
替换为 WHERE category_id IN (37, 43)
并且查询变得更快:
我唯一能想到的是,子查询不是 运行 查询结果,而是在完成某些过滤之前。欢迎任何形式的解释或帮助!
编辑: 愚蠢的我,WHERE IN
不起作用,因为它不产生独特的 select 离子。不过问题仍然存在!
创建 table 架构
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;
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
,当(如果)找到这样的行时停止。
- 一些其他索引,或没有索引:扫描整个 table,检查每一行是否有
x=1 AND y=2
;将它们收集到临时 table 中;按 id
排序;送一排。
一些 EXPLAIN
线索:
- 使用where——不多说
- 使用文件排序 -- 它进行了排序,显然是针对
ORDER BY
。 (它可能完全在 RAM 中完成;忽略 'file'。)
- Using index condition (not "Using index") -- 这表示内部优化,它可以比旧版本更有效地检查
WHERE
子句。
不要相信EXPLAIN
中的"Rows"。通常它们是相当正确的,但有时它们会相差几个数量级。这是查看 "how much work" 正在以相当快的查询完成的更好方法:
FLUSH STATUS;
SELECT ...;
SHOW SESSION STATUS LIKE 'Handler%';
有了CREATE TABLE
,我可能对如何改进索引有建议。
我正在优化我的查询,发现了一些我无法理解的东西。
我正在对 select 一堆类别使用以下查询,将它们与来自 table 的别名组合,其中包含类别的旧别名和新别名:
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
的值为 11 的类别只有 2 个,因此它应该从别名 table 中查找 2 个类别。
仍然,如果我使用 EXPLAIN
,它说它必须处理 48 行。别名 table 每个类别也包含 1 个条目(在这种情况下,它可以更多)。所有内容都已编入索引,如果我理解正确,它应该会立即找到正确的别名。
奇怪的是。当我不按条件的类别比较别名,而是手动按类别 ID 查询 returns 时,它只处理 1 行,如索引所预期的那样。
所以我将 WHERE category_id = c.id
替换为 WHERE category_id IN (37, 43)
并且查询变得更快:
我唯一能想到的是,子查询不是 运行 查询结果,而是在完成某些过滤之前。欢迎任何形式的解释或帮助!
编辑: 愚蠢的我,WHERE IN
不起作用,因为它不产生独特的 select 离子。不过问题仍然存在!
创建 table 架构
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;
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
,当(如果)找到这样的行时停止。 - 一些其他索引,或没有索引:扫描整个 table,检查每一行是否有
x=1 AND y=2
;将它们收集到临时 table 中;按id
排序;送一排。
一些 EXPLAIN
线索:
- 使用where——不多说
- 使用文件排序 -- 它进行了排序,显然是针对
ORDER BY
。 (它可能完全在 RAM 中完成;忽略 'file'。) - Using index condition (not "Using index") -- 这表示内部优化,它可以比旧版本更有效地检查
WHERE
子句。
不要相信EXPLAIN
中的"Rows"。通常它们是相当正确的,但有时它们会相差几个数量级。这是查看 "how much work" 正在以相当快的查询完成的更好方法:
FLUSH STATUS;
SELECT ...;
SHOW SESSION STATUS LIKE 'Handler%';
有了CREATE TABLE
,我可能对如何改进索引有建议。