2012-02-14 3 views
7

Я размышлял об этом некоторое время, и он дошел до точки, где я думаю, что лучше спросить и послушать то, что думают другие люди.Точная разбивка на страницы с левыми совпадениями

Im bulding система, которая хранит местоположения на Mysql. Каждое место имеет тип, и некоторые местоположения имеют несколько адресов.

Таблицы выглядеть примерно так

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 

Так что, если я хотел запросить базу данных для 10 последних добавленных я бы с чем-то вроде этого:

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 

Правильно? Но проблема в том, что если местоположение имеет более 1 адреса, ограничение/разбиение на страницы не будет совпадать, если только я не «GROUP BY l.location_id», но это будет показывать только один адрес для каждого места .. что происходит с местами, которые имеют несколько адресов?

Так что я подумал, что единственный способ решить эту проблему, делая запрос внутри цикла .. Что-то вроде этого (псевдокод):

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

} 

Теперь им получать последние 10 мест, но, делая это, я завершите работу не менее 10 запросов, и я не думаю, что это помогает производительности приложения.

Есть ли лучший способ достичь того, что они ищут? (точная разбивка на страницы).

+0

Какой адрес (локальная запись) вы хотите вернуть для местоположения? Если вы можете сказать, какой из них вы хотите, мы можем сообщить компьютеру, какой вы хотите. –

ответ

17

Вот исходный запрос

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 

Вы выполняете разбиение на страницы последнего. Если вы реорганизуете этот запрос, вы можете выполнить разбивку на страницы раньше.

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

Уведомление: Я создал подзапрос под названием k. 10 ключей получают и заказали FIRST !!!

Тогда JOINs могут продолжаться оттуда, надеясь, используя только 10 location_ids.

Что поможет подзапросу k является индексом, который несет LOCATION_ID и location_type_id

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

Вот кои-что еще вы можете, как об этом подходе

Как вы запрашиваете в течение следующих 10 идентификаторов (идентификаторы 11-20)? Как это:

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

Все, что вам нужно сделать, это изменить положение в подзапросе kLIMIT с каждой новой страницей.

  • LIMIT 20,10
  • LIMIT 30,10
  • и так далее ...

можно улучшить рефакторинг путем удаления таблицы местоположения и имеют подзапрос K нести необходимые поля, как это:

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

Сделать этот дополнительный указатель необязательным для этой версии.

Дайте ему попробовать !!!

+0

Учитывая тот факт, что использование увеличения LIMIT может быть дорогостоящим после нескольких страниц, основная идея хорошая. – Alfabravo

+0

Спасибо! Это тот тип запроса, который я искал, и никогда не приходил в голову сделать такой подзапрос! – mpratt

2

Есть несколько способов решить эту проблему:

  • Вы можете добавить IsPrimary битную колонку в location_information таблицы, и добавить триггер, чтобы обеспечить каждое место всегда имеет только один location_information запись с этим набором к 1 .
  • Вы можете выбрать самую старую или новейшую запись location_information (MIN/MAX), используя столбец location_id, если у вас нет столбцов DateCreated или DateModified.
3

лучше, чем зацикливание и 10 запросов, вы можете запросить предел location.location_id 10 для пагинации, конкатенации, что в запятой строки, а затем полный запрос, чтобы получить WHERE location.location_id IN (1,2,3...{list of ids})

3

Вы могли бы пойти с ваша оригинальная мысль о группировке с помощью location_id, а затем используйте функцию group_concat, чтобы отобразить все адреса для этого местоположения как 1 поле.

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 
Смежные вопросы