Reputation: 81
Imagine I have a table contains all the chapters of a book and the start/end page from each chapter.
chapter | start_page | end_page
--------------------------------------
1 | 1 | 24
2 | 25 | 67
3 | 68 | 123
4 | 124 | 244
5 | 245 | 323
I'm attempting to find out what chapter a random page falls on, let's say page 215 for example.
My first idea was to use a query like this
SELECT `chapter`
FROM `book`
WHERE `start_page` <= 215
AND `end_page` >= 215
Unfortunately MySQL can not take advantage of indexes in the above query which is a large problem due to the large size of my table.
After doing some research I came up with this query which does take advantage of indexes.
SELECT `chapter`
FROM `book`
WHERE `start_page` <= 215
ORDER BY `start_page` DESC
LIMIT 1
The issue now is I want the ability to query multiple random pages while still taking advantage of indexes. It doesn't seem likely that I can modify my last query since it's so heavily reliant on limiting results to one.
Any advice would be much appreciated!
UPDATE: Thanks to a comment from Ray Toal I have a query which gives me the results I need with amazing performance.
SELECT chapter
FROM book
WHERE (start_page = (SELECT max(start_page) FROM book WHERE start_page <= 73) AND end_page >= 73)
OR (start_page = (SELECT max(start_page) FROM book WHERE start_page <= 92) AND end_page >= 92)
OR (start_page = (SELECT max(start_page) FROM book WHERE start_page <= 300) AND end_page >= 300)
Upvotes: 8
Views: 1431
Reputation: 425063
Isn't it as simple as this?
select max(chapter)
from book
where start_page <= 215;
If end pages follow previous start pages, this will work.
Upvotes: 1
Reputation: 12801
Syntactically-valid equivalent of Bohemian's INTERSECT
solution (requires a unique index of some kind and large join buffer):
SELECT
chapter
FROM
book AS book_l
JOIN book AS book_r
USING (id)
WHERE
book_l.start_page <= 215
AND book_r.end_page >= 215;
Or a temptable approach (requires a single index on each of start_page and end_page):
SELECT chapter FROM (
SELECT * FROM book WHERE start_page <= 215
UNION
SELECT * FROM book WHERE end_page >= 215
) AS derived WHERE start_page <= 215 AND end_page >= 215
Upvotes: 0
Reputation: 12801
Add two composite indices:
ALTER TABLE book
ADD INDEX `page_range_from_start` (start_page, end_page)
ADD INDEX `page_range_from_end` (end_page, start_page)
And proceed with your original query:
SELECT `chapter`
FROM `book`
WHERE
`start_page` <= 215
AND `end_page` >= 215
MySQL will choose the index leading with the column that will give it the fewest remaining rows to scan, and then it will have a second part of the index to reduce to the single desired row (without a scan).
Upvotes: 0