MYSQL: Вместо использования операторов SELECT в цикле while - какие у меня есть варианты? - PullRequest
2 голосов
/ 06 августа 2010

У меня есть таблица, которая содержит список категорий, и еще одна таблица, которая содержит список продуктов в каждой категории.

, например

CatID | Cat_Name
----------------
1     | Books
2     | CDs

и

ProductID | Product_Name     | CatID
------------------------------------
1         |The Bible         | 1
2         |The Koran         | 1
3         |90s Greatest Hits | 2
4         |80s Greatest Hits | 2

что я хочу сделать, это получить

<ul>
<li>Books</li>
   <ul>
   <li>The Bible</li>
   <li>The Koran</li>
   </ul>
<li>Cds</li>
   <ul>
   <li>90s Greatest Hits </li>
   <li>00s Greatest Hits </li>
   </ul>
 </ul>

без выполнения (PHP)

$query = mysql_query("SELECT ... FROM categories")

while($row = mysql_fetch_assoc($query)):

$query2 = mysql_query("SELECT ... FROM products WHERE catId = $row['CatId'])

endwhile;

Как я могу отказаться от использования вложенных операторов while / SELECT, могу ли я сделать это эффективно одним запросом - и как мне получить доступ к соответствующим данным с помощью PHP?

РЕДАКТИРОВАТЬ: У меня сложилось впечатление, что если ваши таблицы становятся достаточно большими, несколько запросов SQL будет замедлять вашу страницу, и поэтому для оптимизации загрузки страницы вы должны попытаться уменьшить количество запросов?

Ответы [ 4 ]

6 голосов
/ 06 августа 2010

выберите все товары и названия категорий, используя JOIN

select * from category c, products p where c.catID = p.catID order by p.catID

выборка и групповые результаты

$products_by_cat = array();
foreach(fetch(....) as $record)
    $products_by_cat[$record->catName][] = $record;

вы получите двумерный массив, подобный

 [books] => array(bible, koran)
 [CDs]   => array(cd1, cd2, ....)

выводить этот массив с двумя вложенными циклами

 foreach($products_by_cat as $cat => $products)
    <li> $cat  <ul>
    foreach($products as $p)
        <li> $p->name </li>
    </li></ul>
2 голосов
/ 06 августа 2010

Вы можете выбрать все данные одновременно, используя объединение ...

SELECT *
    FROM category JOIN products ON catgegory.CatID = products.CatID
    ORDER BY products.CatID;

но я не вижу ничего страшного в вашем текущем подходе.

1 голос
/ 06 августа 2010

user187291 ответ очень хороший. Я хотел бы отметить, что вы можете напечатать свой список, не просматривая набор результатов более одного раза. Это может быть полезно, если у вас много предметов.

Для этого убедитесь, что результат запроса упорядочен по категориям. Создайте переменную для отслеживания «текущей категории» и задайте для нее значение, которое не будет идентификатором категории, например «x». Зацикливайтесь на результате, и каждый раз, когда CatID не соответствует значению вашей текущей категории, вы знаете, что изменили категории, и вам нужно напечатать заголовок категории. Сброс текущего значения категории до нового значения CatID.

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

Имея всего несколько категорий, действительно не имеет значения, как вы это делаете. Если у вас есть тысячи строк с большим количеством данных, этот метод может быть немного быстрее и использовать меньше памяти. Это немного сложнее понять логически, чем метод user187291. Но он просматривает данные только один раз и не создает дубликата данных, что может быть полезно, если у вас есть мегабайты содержимого для циклического прохождения.

0 голосов
/ 06 августа 2010
SELECT * FROM category JOIN products USING(CatID);
...