2012-02-14 18 views
7

Ho pensato a questo per un po 'ed è arrivato a un punto in cui penso sia meglio chiedere in giro e ascoltare cosa pensano gli altri.Impaginazione accurata con giunzioni a sinistra

Im bulding un sistema che memorizza le posizioni su Mysql. Ogni località ha un tipo e alcune località hanno più indirizzi.

Le tabelle sembrano qualcosa di simile

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 

Quindi, se ho voluto interrogare il database per il 10 più recentemente aggiunti Vorrei andare con qualcosa di simile:

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 

Destra? Ma il problema è che se una posizione ha più di 1 indirizzo il limite/impaginazione non accadrà, a meno che io "GROUP BY l.location_id", ma che sta per mostrare solo un indirizzo per ogni posto .. cosa succede con i posti che hanno più indirizzi?

Così ho pensato l'unico modo per risolvere questo è quello di eseguire una query all'interno di un ciclo .. Qualcosa di simile a questo (pseudocodice):

$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); 

} 

Ora Im ottenendo gli ultimi 10 posti, ma facendo che ho finire con almeno 10 query in più, e non penso che questo aiuti le prestazioni dell'app.

C'è un modo migliore per ottenere ciò che sto cercando? (impaginazione accurata).

+0

Quale indirizzo (record_informazioni locali) si desidera restituire per un percorso? Se riesci a dire quale vuoi, possiamo dire al computer quale vuoi. –

risposta

17

Qui è la vostra query originale

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 

Si esegue l'impaginazione scorso. Se si rifatta questa query, è possibile eseguire l'impaginazione in precedenza.

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) 
; 

Avviso Ho creato una sottoquery chiamata k. Le 10 chiavi vengono raccolte e ordinate PRIMA !!!

Quindi i JOIN possono andare avanti da lì, sperare di utilizzare solo 10 location_id.

Cosa vi aiuterà la subquery k è un indice che trasporta location_id e location_type_id

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

Ecco un'altra cosa che hanno la possibilità di questo approccio

Come la tua ricerca per i prossimi 10 ids (ids 11 - 20)? Come questo:

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) 
; 

Tutto quello che dovete fare è cambiare la clausola LIMIT in subquery k con ogni nuova pagina.

  • LIMIT 20,10
  • LIMIT 30,10
  • e così via ...

posso migliorare il refactoring rimuovendo il tavolo posizione e hanno subquery k portano i campi necessari come questo:

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) 
; 

Fare quell'indice extra non sarebbe necessario per questa versione.

Provalo !!!

+0

Dato che l'utilizzo di un LIMIT crescente potrebbe essere costoso dopo un numero di pagine, l'idea principale è buona. – Alfabravo

+0

Grazie! Questo è il tipo di query che stavo cercando, non mi è mai venuto in mente di fare una sottoquery come questa! – mpratt

2

ci sono alcuni modi per risolvere questo:

  • Si potrebbe aggiungere una colonna IsPrimary po 'al tavolo location_information, e aggiungere un trigger per garantire che ogni località ha sempre un solo location_information record con questo set a 1 .
  • È possibile selezionare la più vecchia o più recente location_information registrazione (min/max), utilizzando la colonna location_id se non si hanno DateCreated o DateModified colonne.
3

meglio di loop e 10 query, si può interrogare per il limite location.location_id 10 per l'impaginazione, concatenare che in una stringa separati da virgola e quindi la query completo per ottenere WHERE location.location_id IN (1,2,3...{list of ids})

3

Si potrebbe andare con il tuo pensiero originale sul raggruppamento da location_id e quindi utilizzare la funzione group_concat per visualizzare tutti gli indirizzi per quella posizione come 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