这是一个大约需要1毫秒才能完成的mysql查询:
SELECT SQL_NO_CACHE DISTINCT invoice.*,
GROUP_CONCAT(DISTINCT line.guid) as line
FROM invoice
LEFT JOIN ligne ON line.invoice = invoice.guid
GROUP BY invoice.guid
WHERE acompte = 1
LIMIT 0, 100;
如果我将ORDER BY invoice.date
添加到查询中,它会变得非常慢并且需要大约3秒才能完成。
如果我删除LEFT JOIN
(以及GROUP_CONCAT
),则查询会再次花费1ms。
我添加EXPLAIN
以查看MySQL在查询速度缓慢时正在做什么,我可以看到它正在使用临时文件:
1 SIMPLE invoice index NULL PRIMARY 4 NULL 25385 Using temporary; Using filesort
1 SIMPLE line ref invoice invoice 5 gestixi.invoice.guid 1 Using index
我确信有一种方法可以加快查询速度,但我无法找到它。有什么想法吗?
请注意,我无法在date
上添加索引(顺便说一下,这不会改变任何内容)因为我希望我的用户能够对表的每个字段进行排序。
另请注意,invoice.guid
,line.invoice
,line.guid
和acompte
已编入索引。
如果我在没有LEFT JOIN的情况下进行第一次查询但是使用ORDER BY子句来获取我想要的行的ID,然后在WHERE子句中使用这些id进行第二次查询(如上所述),我可以在不到10ms的时间内得到我需要的东西。
这让我相信它必须是一种在不添加索引的情况下加速查询的方法。
答案 0 :(得分:1)
我担心如果你必须允许你的用户对任何字段进行排序(并且这种排序使用索引),那么你需要为每种可能的排序索引。根据定义,不可能做到这一点。对给定行进行排序只能使用此行上的索引。
我在这里看到的选择很少。要么减少要排序的行数(25k行的结果集有点大,你的用户真的需要那么多行吗?)或者不允许对所有行进行排序。
请注意,查询通常不能够按表使用多个索引。正如其他人所建议的那样,复合索引对于你提到的查询更好,尽管我宁愿建议相反的顺序((guid, date)
)(查询首先需要选择每个guid
,然后,对于每个其中,对相应的行进行排序。)
还在line(guid, acompte, invoice)
上添加索引。
(以上关于索引假设MyISAM表的建议)
在优化查询本身方面,考虑到简单的执行计划,几乎没有什么可做的。
使用此版本可能会获得更好的结果,或者您可能不会:
SELECT
invoice.*, -- DISTINCT is redudant here because of the GROUP BY clause
GROUP_CONCAT(ligne_acompte.guid) as line -- DISTINCT is (presumably) redundant here because guid is (presumably) unique
FROM invoice
LEFT JOIN (
SELECT guid, invoice
FROM line
WHERE acompte = 1
) AS ligne_acompte ON ligne_acompte.invoice = invoice.guid
GROUP BY invoice.guid
ORDER BY invoice.date;