views:

60

answers:

2

I have 10k forum topics. I run this query:

  SELECT * 
    FROM `topics` 
   WHERE category_id = '2' 
ORDER BY `last_message` DESC 
   LIMIT 2990, 10

This checks all rows! All rows! Even after adding index to last_message. last_message is a time() and I want to order all messages by that descending. So, what do you suggest me? Without BETWEEN, because it can't be done with that, unfortunately.

+2  A: 

To get MySQL to use late row lookups, use:

  SELECT t.*
    FROM TOPICS t
    JOIN (SELECT * 
            FROM `topics` 
           WHERE category_id = '2' 
        ORDER BY `last_message` DESC 
           LIMIT 2990, 10) x ON x.topic_id = t.topic_id
ORDER BY t.topic_id

Without TOPICS column details, I have to assume there is an id column that is auto_increment for the table...

You can read more about the behavior in this article.

OMG Ponies
Can you explain it a little bit, because there guy doesn't explain this query and I don't get it at all.
hey
@hey: The derived table (alias "x") is joined back to the TOPICS table, to get the records out of the TOPICS table based on the JOIN criteria.
OMG Ponies
+2  A: 

Since you have a filtering condition on category_id, an index on last_message most probably won't help you much.

The query will have to traverse the index and filter the records for category_id = 2. Since traversing an index is more expensive than scanning the table, a filesort can be a more efficient solution, and MySQL may prefer it over the index scan (that's most probably is what is happening in your case).

Also, as @OMG Ponies pointed out, MySQL is not capable of doing late row lookups.

Assuming that your PRIMARY KEY column is called id, you need to create an index on (category_id, last_message, id) and rewrite your query as follows:

SELECT  t.*
FROM    (
        SELECT  id
        FROM    topics
        WHERE   category_id = 2
        ORDER BY
                category_id DESC, last_message DESC, id DESC
        LIMIT 2990, 10
        ) q
JOIN    topics t
ON      t.id = q.id
Quassnoi
Thank you, it works. But... I absolutely don't understand how this query works and I really would like to get it.
hey
@hey: @OMGPonies provided a link to an article that explains it. In a couple of words, the join makes `MySQL` to skip the first `2,990` records in a more efficient way, making it not to perform some unneeded steps.
Quassnoi
@OMG Ponies, @Quassnoi: We use joins to join two or more tables and we choose `SELECT * FROM 'TABLE'` and here we do absolutely something else, though it works, but still, I want to understand it.
hey
@hey: did you read the article?
Quassnoi
@Quassnoi: Yes, absolutely. Didn't help, because he doesn't explain the query.
hey
@hey: "He" is me, I'm the author of the article. Actually, the whole article is dedicated to explaining the query. To understand it, you need to have some background knowledge though: what is a table, what is an index and how they work together.
Quassnoi