【发布时间】:2018-02-02 13:27:16
【问题描述】:
这需要大约 100 秒来执行:
SELECT part.*
FROM (((((((`part`
LEFT JOIN `engine_x_category_1_x_category_2_x_part`
ON `engine_x_category_1_x_category_2_x_part`.`part_id` =
`part`.`id`)
LEFT JOIN `engine_x_category_1_x_category_2`
ON
`engine_x_category_1_x_category_2_x_part`.`engine_x_category_1_x_category_2_id`
= `engine_x_category_1_x_category_2`.`id`)
LEFT JOIN `engine_x_category_1`
ON `engine_x_category_1_x_category_2`.`engine_x_category_1_id` =
`engine_x_category_1`.`id`)
LEFT JOIN `engine`
ON `engine_x_category_1`.`engine_id` = `engine`.`id`)
LEFT JOIN `model`
ON `engine`.`model_id` = `model`.`id`)
LEFT JOIN `year`
ON `model`.`year_id` = `year`.`id`)
LEFT JOIN `make`
ON `year`.`make_id` = `make`.`id`)
LEFT JOIN `category_1`
ON `engine_x_category_1`.`category_1_id` = `category_1`.`id`
WHERE 1 > 0
AND `category_1`.`id` > 0
GROUP BY `part`.`id`
ORDER BY `part`.`id` ASC
LIMIT 0, 20
然而,一旦我们摆脱了AND category_1.id > 0,它只需要 0.003 秒。
这真的很奇怪,因为 category_1.id 是 PRIMARY KEY 列,而表 category_1 只有 26 行。
category_1的结构和键
查询解释:
这里可能有什么问题?为什么 MySQL 不对category_1.id 使用 PRIMARY 索引,而是使用用于时间戳列的索引 ts?
附: MySQL 版本:5.6.33
更新 1
正如 O. Jones 指出的那样,我尝试使用表 category_1 使用 FORCE INDEX (PRIMARY),并且 EXPLAIN 显示查询正确地使用 PRIMARY 而不是 ts 作为 category_1.id 的索引,但查询仍然需要将近 100 秒才能执行。
似乎category_1.id 不是这里的罪魁祸首。
更新 2
在@O.Jones 的建议下,我去掉了make、year、model 和engine 上不相关的JOIN:
SELECT part.*
FROM (((`part`
LEFT JOIN `engine_x_category_1_x_category_2_x_part`
ON `engine_x_category_1_x_category_2_x_part`.`part_id` =
`part`.`id`)
LEFT JOIN `engine_x_category_1_x_category_2`
ON
`engine_x_category_1_x_category_2_x_part`.`engine_x_category_1_x_category_2_id`
= `engine_x_category_1_x_category_2`.`id`)
LEFT JOIN `engine_x_category_1`
ON `engine_x_category_1_x_category_2`.`engine_x_category_1_id` =
`engine_x_category_1`.`id`)
LEFT JOIN `category_1`
ON `engine_x_category_1`.`category_1_id` = `category_1`.`id`
WHERE 1 > 0
AND `category_1`.`id` > 0
GROUP BY `part`.`id`
ORDER BY `part`.`id` ASC
LIMIT 0, 20
现在查询只需要大约 0.003 秒。
虽然这回答了这个特定查询的问题,但它显然 不是 最终解决方案,因为人们可能还需要通过 both @ 987654348@ 和 make,因此无法删除查询中的任何 JOIN:
SELECT part.*
FROM (((((((`part`
LEFT JOIN `engine_x_category_1_x_category_2_x_part`
ON `engine_x_category_1_x_category_2_x_part`.`part_id` =
`part`.`id`)
LEFT JOIN `engine_x_category_1_x_category_2`
ON
`engine_x_category_1_x_category_2_x_part`.`engine_x_category_1_x_category_2_id`
= `engine_x_category_1_x_category_2`.`id`)
LEFT JOIN `engine_x_category_1`
ON `engine_x_category_1_x_category_2`.`engine_x_category_1_id` =
`engine_x_category_1`.`id`)
LEFT JOIN `engine`
ON `engine_x_category_1`.`engine_id` = `engine`.`id`)
LEFT JOIN `model`
ON `engine`.`model_id` = `model`.`id`)
LEFT JOIN `year`
ON `model`.`year_id` = `year`.`id`)
LEFT JOIN `make`
ON `year`.`make_id` = `make`.`id`)
LEFT JOIN `category_1`
ON `engine_x_category_1`.`category_1_id` = `category_1`.`id`
WHERE 1 > 0
AND `category_1`.`id` > 0
AND `make`.`id` > 0
GROUP BY `part`.`id`
ORDER BY `part`.`id` ASC
LIMIT 0, 20
经过多次测试,仍然可以运行大约 80 - 100 秒。
我们现在应该做什么?
更新 3
按照@LuisMuñoz 的建议,我将条件从 WHERE 移至相应的 JOIN ON 子句:
SELECT part.*
FROM (((((((`part`
LEFT JOIN `engine_x_category_1_x_category_2_x_part`
ON `engine_x_category_1_x_category_2_x_part`.`part_id` =
`part`.`id`)
LEFT JOIN `engine_x_category_1_x_category_2`
ON
`engine_x_category_1_x_category_2_x_part`.`engine_x_category_1_x_category_2_id`
= `engine_x_category_1_x_category_2`.`id`)
LEFT JOIN `engine_x_category_1`
ON `engine_x_category_1_x_category_2`.`engine_x_category_1_id` =
`engine_x_category_1`.`id`)
LEFT JOIN `engine`
ON `engine_x_category_1`.`engine_id` = `engine`.`id`)
LEFT JOIN `model`
ON `engine`.`model_id` = `model`.`id`)
LEFT JOIN `year`
ON `model`.`year_id` = `year`.`id`)
JOIN `make`
ON `year`.`make_id` = `make`.`id`
AND `make`.`id` > 0)
JOIN `category_1`
ON `engine_x_category_1`.`category_1_id` = `category_1`.`id`
AND `category_1`.`id` > 0
WHERE 1 > 0
GROUP BY `part`.`id`
LIMIT 0, 10
必须使用 JOIN 而不是 LEFT JOIN 否则条件不会生效。
但是它仍然和 WHERE 子句中的条件一样慢。
【问题讨论】:
-
请格式化查询。不可读。
-
engine_x_category_1.category_1_id上是否有索引,查询正在扫描整个 category_1 表 4319/26 次。 -
@LuisMuñoz 是的,
engine_x_category_1.category_1_id被索引为非唯一索引。 -
SELECT part.id AS part.id, .... part.category_2 AS part.category_2 .... GROUP BY part.id是无效的 SQL 查询并导致无效的结果。阅读(psce.com/en/blog/2012/05/15/…) -
category_1表上的索引无关紧要。它的行数太少,以至于索引访问无关紧要。您的parts表上的索引可能会有所不同,也许在那些具有长名称的_category_表上。请注意,您的LEFT JOIN到category_1通过引用WHERE子句中的列转换为普通的内部JOIN。您的查询SELECTs 在您的LEFT JOIN操作中提到的某些表中没有列,因此您至少可以简化查询以进行性能故障排除。
标签: mysql database performance query-performance