I have a materialized path-driven bulletin board. It is using the following query to get the messages in order,
SELECT * FROM Board ORDER by root DESC, path ASC LIMIT 0,100
where root is an id of the root message for the thread, and path is a materialized path.
However, none of my efforts to make this query to use indexes were of any success.
mysql> explain extended select path from Board order by root desc, path asc limit 100;
+-------+---------------+----------+---------+------+-------+----------+----------------------------+
| type | possible_keys | key | key_len | ref | rows | filtered | Extra
+-------+---------------+----------+---------+------+-------+----------+-----------------------------
| index | NULL | rootpath | 261 | NULL | 21998 | 100.00 | Using index; Using filesort
At the moment it is showing the number of all the rows in the table under rows column. I am wondering, is there a way to reduce that number or optimize the query any other way?
CREATE TABLE `Board` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`path` varchar(255) NOT NULL DEFAULT '0',
`root` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `root` (`root`),
KEY `path` (`path`),
KEY `rootpath` (`root`,`path`)
)
The main problem with the query is pagination – I need to start the second page right from the message next to the last one on the previous page. That’s why I want it the straight way – without sublelects and stuff.
The current setup is not quite nice though, as it starts the second page from the middle of the thread, but it is quite logical at least.
Your original query
Create a table to hold the negative value of root called BoardDisplayOrder, where you add the new column called rootinv.
First here is the sample data and your original query:
Next, create the table BoardDisplayOrder using rootinv and an index involving rootinv:
Then, populate BoardDisplayOrder:
Now, run your query against BoardDisplayOrder but without DESC on rootinv:
Give it a try!!!
CAVEAT
This was easy to do because root was INT.
If root was a VARCHAR, rootinv would have to be a flipflop of characters. In other words,
A->ZB->YM->NN->MY->BZ->AThis would principly work for any field you need to perform DESC on. The problem stems from the fact that MySQL does not order keys internally within in index as ASC or DESC. Everything in an index is ascending. That is why when you see handler stats in
SHOW GLOBAL STATUS LIKE 'handler%';, you see the following:and so forth.
According to the current MySQL Documentation
Give it a try!!!
UPDATE 2012-05-04 06:54 EDT
@frail’s comment about my answer
The reason my solution had
ALTER TABLE BoardDisplayOrder ADD INDEX rootpathid (rootinv,path,id,root)is to provide a covering index. A covering index in this instance will:Think of the original query,
This requires retrieving the three columns path, id, and root. Thus, they need to be in the index. Of course, the increased size of the index would be the tradeoff. If the Board table was very large, some would not worry about the space if the retrieval could be made faster. If the rootpath index were just (rootinv,path), then every index range scan would be accompanied by a ref lookup into the table for the remaining columns. This is the reason I chose
ALTER TABLE BoardDisplayOrder ADD INDEX rootpathid (rootinv,path,id,root);