2012-02-14 9 views
7

He estado pensando en esto por un tiempo y llegó a un punto en el que creo que es mejor preguntar y escuchar lo que otras personas piensan.Paginación precisa con uniones hacia la izquierda

Im construyendo un sistema que almacena ubicaciones en Mysql. Cada ubicación tiene un tipo y algunas ubicaciones tienen varias direcciones.

Las tablas tienen el siguiente aspecto

location 
    - location_id (autoincrement) 
    - location_name 
    - location_type_id 

location_types 
    - type_id 
    - type_name (For example "Laundry") 

location_information 
    - location_id (Reference to the location table) 
    - location_address 
    - location_phone 

así que si quería para consultar la base de datos para el 10 añadidas más recientemente me gustaría ir con algo como esto:

SELECT l.location_id, l.location_name, 
     t.type_id, t.type_name, 
     i.location_address, i.location_phone 
FROM location AS l 
LEFT JOIN location_information AS i ON (l.location_id = i.location_id) 
LEFT JOIN location_types AS t ON (l.location_type_id = t.type_id) 
ORDER BY l.location_id DESC 
LIMIT 10 

derecho? Pero el problema es que si una ubicación tiene más de 1 dirección, el límite/paginación no se convertirá en accurrate, a menos que "GROUP BY l.location_id", pero eso mostrará solo una dirección para cada lugar ... ¿qué sucede? con los lugares que tienen varias direcciones?

Así que pensé que la única manera de resolver esto es haciendo una consulta dentro de un bucle .. Algo como esto (pseudocódigo):

$db->query('SELECT l.location_id, l.location_name, 
      t.type_id, t.type_name 
      FROM location AS l 
      LEFT JOIN location_types AS t ON (l.location_type_id = t.type_id) 
      ORDER BY l.location_id DESC 
      LIMIT 10'); 

$locations = array(); 
while ($row = $db->fetchRow()) 
{ 
    $db->query('SELECT i.location_address, i.location_phone 
       FROM location_information AS i 
       WHERE i.location_id = ?', $row['location_id']); 

    $locationInfo = $db->fetchAll(); 
    $locations[$row['location_id']] = array('location_name' => $row['location_name'], 
              'location_type' => $row['location_type'], 
              'location_info' => $locationInfo); 

} 

Ahora Im consiguiendo los últimos 10 lugares, pero al hacer eso me terminar con al menos 10 consultas más, y no creo que eso ayude al rendimiento de la aplicación.

¿Hay alguna manera mejor de lograr lo que estoy buscando? (paginación precisa).

+0

¿Qué dirección (registro de información local) le gustaría devolver para una ubicación? Si puede decir cuál quiere, podemos decirle a la computadora cuál quiere. –

Respuesta

17

Aquí es su búsqueda original

SELECT l.location_id, l.location_name, 
     t.type_id, t.type_name, 
     i.location_address, i.location_phone 
FROM location AS l 
LEFT JOIN location_information AS i ON (l.location_id = i.location_id) 
LEFT JOIN location_types AS t ON (l.location_type_id = t.type_id) 
ORDER BY l.location_id DESC 
LIMIT 10 

Se realiza la paginación pasada. Si refactoriza esta consulta, puede realizar la paginación antes.

SELECT l.location_id, l.location_name, 
     t.type_id, t.type_name, 
     i.location_address, i.location_phone 
FROM 
    (SELECT location_id,location_type_id FROM location 
    ORDER BY location_id LIMIT 10) AS k 
    LEFT JOIN location AS l ON (k.location_id = l.location_id) 
    LEFT JOIN location_information AS i ON (k.location_id = i.location_id) 
    LEFT JOIN location_types AS t ON (l.location_type_id = t.type_id) 
; 

Aviso Creé una subconsulta llamada k. ¡Las 10 llaves se recogen y se ordenan PRIMERO!

A continuación, las UNIONES pueden continuar a partir de allí, esperamos usar solo 10 location_ids.

¿Qué va a ayudar a la sub consulta k es un índice que lleva location_id y location_type_id

ALTER TABLE location ADD INDEX id_type_ndx (location_id,location_type_id); 

Aquí hay algo más que le gusten acerca de este enfoque

¿Cómo se consulta para los próximos 10 ID (identificadores de 11 - 20)? De esta manera:

SELECT l.location_id, l.location_name, 
     t.type_id, t.type_name, 
     i.location_address, i.location_phone 
FROM 
    (SELECT location_id,location_type_id FROM location 
    ORDER BY location_id LIMIT 10,10) AS k 
    LEFT JOIN location AS l ON (k.location_id = l.location_id) 
    LEFT JOIN location_information AS i ON (k.location_id = i.location_id) 
    LEFT JOIN location_types AS t ON (l.location_type_id = t.type_id) 
; 

Todo lo que tiene que hacer es cambiar la cláusula LIMIT en subconsulta k con cada nueva página.

  • LIMIT 20,10
  • LIMIT 30,10
  • y así sucesivamente ...

puedo mejorar la refactorización mediante la eliminación de la tabla de localización y tener k subconsulta llevan los campos necesarios así:

SELECT k.location_id, k.location_name, 
     t.type_id, t.type_name, 
     i.location_address, i.location_phone 
FROM 
    (SELECT location_id,location_type_id,location_name 
    FROM location ORDER BY location_id LIMIT 10,10) AS k 
    LEFT JOIN location_information AS i ON (k.location_id = i.location_id) 
    LEFT JOIN location_types AS t ON (k.location_type_id = t.type_id) 
; 

No sería necesario hacer ese índice adicional para esta versión.

¡Pruébalo!

+0

Dado el hecho de que usar un LÍMITE cada vez mayor podría ser costoso después de varias páginas, la idea principal es buena. – Alfabravo

+0

¡Gracias! Este es el tipo de consulta que estaba buscando, ¡nunca se me pasó por la mente hacer una subconsulta como esta! – mpratt

2

Hay algunas maneras de resolver esto:

  • Se podría añadir una columna IsPrimary poco a la mesa location_information, y añadir un disparador para asegurar cada lugar siempre tiene un solo location_information registro con este conjunto a 1
  • Puede seleccionar el registro location_information más antiguo o más reciente (MIN/MAX), utilizando la columna location_id si no tiene columnas DateCreated o DateModified.
3

mejor que bucle y 10 consultas, se puede consultar para el límite location.location_id 10 para la paginación, que concatenar en una cadena separada por comas y luego la consulta completa para obtener WHERE location.location_id IN (1,2,3...{list of ids})

3

Usted podría ir con su idea original al agrupar por la ubicación_id y luego usar la función group_concat para mostrar todas las direcciones para esa ubicación como 1 campo.

SELECT l.location_id, l.location_name, 
    t.type_id, t.type_name, 
    group_concat(concat("Address: ",i.location_address, " Phone: ", i.location_phone)) as addresses 
FROM location AS l 
LEFT JOIN location_information AS i ON (l.location_id = i.location_id) 
LEFT JOIN location_types AS t ON (l.location_type_id = t.type_id) 
GROUP BY l.location_id 
ORDER BY l.location_id DESC 
LIMIT 10 
Cuestiones relacionadas