如何改进 MySQL 中的 Limit 子句
Posted
技术标签:
【中文标题】如何改进 MySQL 中的 Limit 子句【英文标题】:How to improve Limit clause in MySQL 【发布时间】:2015-04-15 11:34:11 【问题描述】:我有一个有 10k 行的 posts
表,我想通过它创建分页。因此,我为此目的进行了下一个查询:
SELECT post_id
FROM posts
LIMIT 0, 10;
当我 Explain
该查询时,我得到下一个结果:
所以我不明白为什么 mysql 需要遍历 9976 行才能找到前 10 行?如果有人帮助我优化此查询,我将非常感激。
我也知道MySQL ORDER BY / LIMIT performance: late row lookups那个话题,但是即使我把查询修改为下一个,问题依然存在:
SELECT t.post_id
FROM (
SELECT post_id
FROM posts
ORDER BY
post_id
LIMIT 0, 10
) q
JOIN posts t
ON q.post_id = t.post_id
更新
@pala_ 的解决方案非常适合上述简单情况,但现在我正在使用inner join
测试更复杂的查询。我的目的是将评论表与 post 表连接起来,不幸的是,当我解释新查询时仍然遍历 9976 行。
Select comm.comment_id
from comments as comm
inner join (
SELECT post_id
FROM posts
ORDER BY post_id
LIMIT 0, 10
) as paged_post on comm.post_id = paged_post.post_id;
你知道这种 MySQL 行为的原因是什么吗?
【问题讨论】:
为什么你需要一个派生部分并加入为什么不只是一个查询select ..limit
这是他的第一个查询,他正在比较它们
【参考方案1】:
试试这个:
SELECT post_id
FROM posts
ORDER BY post_id DESC
LIMIT 0, 10;
通过LIMIT
进行分页没有任何意义,但它应该可以解决您的问题。
mysql> explain select * from foo;
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
| 1 | SIMPLE | foo | index | NULL | PRIMARY | 4 | NULL | 20 | Using index |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
1 row in set (0.00 sec)
mysql> explain select * from foo limit 0, 10;
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
| 1 | SIMPLE | foo | index | NULL | PRIMARY | 4 | NULL | 20 | Using index |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
1 row in set (0.00 sec)
mysql> explain select * from foo order by id desc limit 0, 10;
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
| 1 | SIMPLE | foo | index | NULL | PRIMARY | 4 | NULL | 10 | Using index |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
1 row in set (0.00 sec)
关于您最近一次关于评论加入的 cmets。你有comment(post_id)
的索引吗?使用我的测试数据,我得到以下结果:
mysql> alter table comments add index pi (post_id);
Query OK, 0 rows affected (0.15 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> explain select c.id from comments c inner join (select id from posts o order by id limit 0, 10) p on c.post_id = p.id;
+----+-------------+------------+-------+---------------+---------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+-------+---------------+---------+---------+------+------+--------------------------+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 10 | |
| 1 | PRIMARY | c | ref | pi | pi | 5 | p.id | 4 | Using where; Using index |
| 2 | DERIVED | o | index | NULL | PRIMARY | 4 | NULL | 10 | Using index |
+----+-------------+------------+-------+---------------+---------+---------+------+------+--------------------------+
以及表格大小参考:
mysql> select count(*) from posts;
+----------+
| count(*) |
+----------+
| 15021 |
+----------+
1 row in set (0.01 sec)
mysql> select count(*) from comments;
+----------+
| count(*) |
+----------+
| 1000 |
+----------+
1 row in set (0.00 sec)
【讨论】:
感谢您的回答!它运作良好。但我还有一个问题。现在我正在使用内部连接测试更复杂的查询。我的目的是将评论表与 post 表连接起来,不幸的是,当我解释新查询时,它仍然遍历 9976 行。 >> 你知道这种 MySQL 行为的原因是什么吗? 尝试不使用子查询,只需将限制添加到查询末尾,即explain select * from posts p inner join comments c on p.post_id = c.post_id order by p.post_id desc limit 0, 10
我使用子查询按帖子分页。如果我删除子查询并像上面提到的那样只使用连接,则分页将中断,我将只从连接表中获取第一行。有没有其他方法可以通过 post 分页来达到结果?
哦。您想要 10 个帖子及其所有 cmets?
我实际上并没有得到和你一样的结果。你有comments
(post_id) 的索引吗?以上是关于如何改进 MySQL 中的 Limit 子句的主要内容,如果未能解决你的问题,请参考以下文章
等效于 SQL SERVER 的 MySQL LIMIT 子句