3

I have the posts table with 10k rows and I want to create pagination by that. So I have the next query for that purpose:

SELECT post_id
    FROM posts
    LIMIT 0, 10;

When I Explain that query I get the next result:

enter image description here

So I don't understand why MySql need to iterate thru 9976 rows for finding the 10 first rows? I will be very thankful if somebody help me to optimize this query.

Also I know about that topic MySQL ORDER BY / LIMIT performance: late row lookups, but the problem still exist even if I modify the query to the next one:

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

enter image description here

Update

@pala_'s solution works great for above simple case but now while I am testing a more complex query with inner join. My purpose is to join comment table with post table and unfortunately when I Explain new query is still iterate through 9976 rows.

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;  

Do you have some idea what is the reason of such MySQL behavior ?

Ejaz
  • 8,719
  • 3
  • 34
  • 49
Speise
  • 789
  • 1
  • 12
  • 28

1 Answers1

5

Try this:

SELECT post_id
    FROM posts
    ORDER BY post_id DESC
    LIMIT 0, 10;

Pagination via LIMIT doesn't make much sense without ordering anyway, and it should fix your problem.

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)

Regarding your last comments about the comment join. Do you have an index on comment(post_id)? with my test data I'm getting the following results:

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              |
+----+-------------+------------+-------+---------------+---------+---------+------+------+--------------------------+

and for table size reference:

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)
pala_
  • 8,901
  • 1
  • 15
  • 32
  • Thank you for the answer! It works well. But I have one more question. Now I am testing more complex query with inner join. My purpose is to join comment table with post table and unfortunately when I Explain new query is still iterate thru 9976 rows. <<< 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; >>> Do you have some idea what is the reason of such MySQL behavior ? – Speise Apr 15 '15 at 12:12
  • try doing it without the subquery, and just add the limit to the end of the query , ie `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` – pala_ Apr 15 '15 at 12:35
  • I use subquery for pagination by posts. If I remove subquery and use just join like you mentioned above the pagination will break and I will get just first rows from joined table. Is there any other way to achieve the result with pagination by post? – Speise Apr 15 '15 at 13:01
  • oh. you want 10 posts and all comments thereof? – pala_ Apr 15 '15 at 13:21
  • i don't actually get the same result as you. do you have an index on `comments`(post_id) ? – pala_ Apr 15 '15 at 13:43
  • Yes I have index on comments(post_id). Just because comments(post_id) is a foreign key so index was created automatically in Innodb. And I have no idea what is the reason of different results =(. – Speise Apr 15 '15 at 14:08
  • can you include the output of 'explain' over that last query? – pala_ Apr 16 '15 at 00:54
  • I moved this new question to the new [branch](http://stackoverflow.com/questions/29654576/how-to-improve-limit-clause-in-mysql-subquery?noredirect=1#comment47458905_29654576) – Speise Apr 16 '15 at 06:55