2011-09-17 6 views
8

Imagine que tengo una tabla que contiene todos los capítulos de un libro y la página de inicio/final de cada capítulo.¿Cómo puedo consultar entre dos columnas sin dejar de aprovechar los índices?

chapter | start_page  | end_page 
-------------------------------------- 
    1 |  1   | 24 
    2 |  25  | 67 
    3 |  68  | 123 
    4 |  124  | 244 
    5 |  245  | 323 

Estoy tratando de averiguar en qué capítulo cae una página al azar, digamos página 215 por ejemplo.

Mi primera idea era utilizar una consulta como esta

SELECT `chapter` 
FROM `book` 
WHERE `start_page` <= 215 
AND `end_page` >= 215 

Desafortunadamente MySQL no puede tomar ventaja de los índices de la consulta anterior que es un problema grande debido al gran tamaño de mi mesa.

Después de investigar un poco, se me ocurrió esta consulta que aprovecha los índices.

SELECT `chapter` 
FROM `book` 
WHERE `start_page` <= 215 
ORDER BY `start_page` DESC  
LIMIT 1 

El problema ahora es que quiero la capacidad de consultar varias páginas al azar sin dejar de aprovechar los índices. No parece probable que pueda modificar mi última consulta, ya que depende en gran medida de limitar los resultados a uno.

¡Cualquier consejo sería muy apreciado!

ACTUALIZACIÓN: Gracias a un comentario de Ray Toal Tengo una consulta que me da los resultados que necesito con un rendimiento increíble.

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

¿Desea la idea de que, en una consulta, envíe varias páginas y obtenga como resultado una tabla con números de página vinculados con su capítulo? –

+0

Solo necesito una tabla de números de capítulo en el resultado. No necesito que aparezcan con los números de página. – Chip

+0

Entonces, de alguna manera, desea enviar un conjunto de números de página como 73, 92, un 300 y desea obtener los números 3 y 5, ¿correcto? –

Respuesta

0

Añadir dos índices compuestos:

ALTER TABLE book 
    ADD INDEX `page_range_from_start` (start_page, end_page) 
    ADD INDEX `page_range_from_end` (end_page, start_page) 

y proceder con su búsqueda original:

SELECT `chapter` 
FROM `book` 
WHERE 
    `start_page` <= 215 
    AND `end_page` >= 215 

MySQL elegirá el índice anticipado con la columna que le dará las filas restantes menor número de para escanear, y luego tendrá una segunda parte del índice para reducir a la única fila deseada (sin un escaneo).

+0

Intenté esto con mi primera consulta originalmente. MySQL no aprovecha muy bien esos índices y mis consultas promedian un par de segundos debido al tamaño de mi tabla. – Chip

+0

En comparación, mi segunda consulta promedia .0005 segundos en la misma tabla. El problema es, por supuesto, el hecho de que solo puedo consultar una página a la vez. – Chip

+0

Interesante. ¿Forzar un índice tiene algún impacto? –

0

equivalentes sintácticamente válida de la solución de Bohemia INTERSECT (requiere un índice único de algún tipo y gran buffer de unión):

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; 

O un enfoque TempTable (requiere un único índice en cada uno de START_PAGE y 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 
+0

Intenté ambas consultas con los índices. La primera consulta parece promediar alrededor de un segundo, mientras que la segunda promedia unos 10 segundos. – Chip

1

¿No es así de simple?

select max(chapter) 
from book 
where start_page <= 215; 

Si las páginas finales siguen las páginas de inicio anteriores, esto funcionará.

Cuestiones relacionadas