2009-08-07 11 views

Respuesta

13

Quizás podría crear una tabla de indización que proporcione una clave secuencial relacionada con la clave en su tabla de destino. Luego puede unir esta tabla de indexación a su tabla de destino y usar una cláusula where para obtener de manera más eficiente las filas que desea.

#create table to store sequences 
CREATE TABLE seq (
    seq_no int not null auto_increment, 
    id int not null, 
    primary key(seq_no), 
    unique(id) 
); 

#create the sequence 
TRUNCATE seq; 
INSERT INTO seq (id) SELECT id FROM mytable ORDER BY id; 

#now get 1000 rows from offset 1000000 
SELECT mytable.* 
FROM mytable 
INNER JOIN seq USING(id) 
WHERE seq.seq_no BETWEEN 1000000 AND 1000999; 
+3

este enfoque solo funciona en sentencias seleccionadas que no contienen el estado. en mi opinión, no es una buena solución. –

+3

¿Cómo mantener esta tabla de índice actualizada? En mi caso, tengo que ordenar por columna de fecha y hora y usar compensaciones grandes que resultan en consultas lentas. Si creo esta tabla de soporte, tendré que volver a insertar cada vez que tenga una nueva fecha, ya que no está en orden. Ya veo esta solución, pero con tablas temporales. –

9

Hay una entrada en el blog algún lugar de Internet sobre cómo debe mejor hacer la selección de las filas para mostrar debería ser lo más compacto posible, por lo tanto: sólo los identificadores; y la producción de los resultados completos debería, a su vez, obtener todos los datos que desee solo para las filas que seleccionó.

Por lo tanto, el SQL podría ser algo como (no probado, no estoy seguro de lo que realmente va a hacer ningún bien):

select A.* from table A 
    inner join (select id from table order by whatever limit m, n) B 
    on A.id = B.id 
order by A.whatever 

Si el motor SQL es demasiado primitiva para permitir este tipo de sentencias SQL, o no mejora nada, contra toda esperanza, podría valer la pena dividir esta única declaración en varias declaraciones y capturar las identificaciones en una estructura de datos.

Actualización: Encontré la publicación del blog de la que estaba hablando: era Jeff Atwood's "All Abstractions Are Failed Abstractions" en Coding Horror.

+0

He probado su SQL sugerido. pero no mejora. –

+1

¿Qué sucede si tiene una cláusula Where basada en la tabla A? No funcionará, ya que primero limita, luego aplica la cláusula where. Si usa join en el interior de su subconsulta, perderá rendimiento, ¿verdad? –

+0

Me funcionó, la consulta 'SELECT id FROM ...' se ejecutó aproximadamente 50 veces más rápido en un conjunto de casi un millón de filas en comparación con 'SELECT bunch, of, fields FROM ...'. –

2

La respuesta de Paul Dixon es realmente una solución al problema, pero deberá mantener la tabla de secuencias y asegurarse de que no haya espacios vacíos.

Si eso es factible, una mejor solución sería simplemente asegurarse de que la tabla original no tenga espacios vacíos y comience desde la identificación 1. Luego tome las filas usando la identificación para la paginación.

SELECCIONAR * DE la tabla A DONDE id > = 1 AND id < = 1000;
SELECCIONAR * FROM tabla A DONDE id > = 1001 AND id < = 2000;

y así sucesivamente ...

+0

SELECCIONAR * FROM tabla WHERE id> 1000 LIMIT 1000 –

+1

Nuevamente, no funcionará si se aplican otros filtros. – devXen

2

No creo que haya ninguna necesidad de crear un índice separado si la tabla ya tiene uno. Si es así, entonces se puede pedir por esta clave primaria y luego usar los valores de la clave para avanzar por:

SELECT * FROM myBigTable WHERE id > :OFFSET ORDER BY id ASC; 

Otra optimización sería no utilizar SELECT *, pero sólo el ID de modo que pueda simplemente leer el índice y no tiene que ubicar todos los datos (reduce la sobrecarga de IO).Si necesita algunas de las otras columnas, entonces tal vez podría agregarlas al índice para que se lean con la clave principal (que probablemente se mantendrá en la memoria y, por lo tanto, no requerirá una búsqueda de disco), aunque esto no será apropiado. para todos los casos, entonces tendrás que jugar.

escribí un artículo con más detalles:

http://www.4pmp.com/2010/02/scalable-mysql-avoid-offset-for-large-tables/

+0

¿Es solo MySQL o MTS DBS actúa de esta manera extraña? Hasta ahora, la mejor solución es la subconsulta (cuando no tienes un índice ordenado). Consulta y ordena todo primero, luego pon el desplazamiento. –

+0

La idea de usar solo la ID puede ser una muy buena solución, ¡depende del motor de almacenamiento, supongo! – twicejr

4

Si los registros son grandes, la lentitud puede venir de la carga de los datos. Si la columna de identificación está indexada, simplemente seleccionarla será mucho más rápido. A continuación, puede hacer una segunda consulta con una cláusula IN para los identificadores apropiados (o podría formular una cláusula WHERE usando el min y ids max de la primera consulta.)

lento:

SELECT * FROM table ORDER BY id DESC LIMIT 10 OFFSET 50000 

rápido:

SELECT id FROM table ORDER BY id DESC LIMIT 10 OFFSET 50000 

SELECT * FROM table WHERE id IN (1,2,3...10) 
0

Me he encontrado recientemente con este problema. El problema fue dos partes para arreglar. Primero tenía que utilizar un selecto interno en mi cláusula FROM que hicieron mi limitación y compensación para mí en la clave principal única:

$subQuery = DB::raw("(SELECT id FROM titles WHERE id BETWEEN {$startId} AND {$endId} ORDER BY title) as t"); 

entonces podría usar eso como el de la parte de mi consulta:

'titles.id', 
          'title_eisbns_concat.eisbns_concat', 
          'titles.pub_symbol', 
          'titles.title', 
          'titles.subtitle', 
          'titles.contributor1', 
          'titles.publisher', 
          'titles.epub_date', 
          'titles.ebook_price', 
          'publisher_licenses.id as pub_license_id', 
          'license_types.shortname', 
          $coversQuery 
         ) 
         ->from($subQuery) 
         ->leftJoin('titles', 't.id', '=', 'titles.id') 
         ->leftJoin('organizations', 'organizations.symbol', '=', 'titles.pub_symbol') 
         ->leftJoin('title_eisbns_concat', 'titles.id', '=', 'title_eisbns_concat.title_id') 
         ->leftJoin('publisher_licenses', 'publisher_licenses.org_id', '=', 'organizations.id') 
         ->leftJoin('license_types', 'license_types.id', '=', 'publisher_licenses.license_type_id') 

La primera vez que creé esta consulta, utilicé OFFSET y LIMIT en MySql. Esto funcionó bien hasta que pasé de la página 100 a continuación, el desplazamiento comenzó a ser insoportablemente lento. Cambiar eso a BETWEEN en mi consulta interna lo aceleró para cualquier página. No estoy seguro de por qué MySql no ha acelerado el DESPLAZAMIENTO, pero parece entrelazarlo de nuevo.

Cuestiones relacionadas