Неэффективный SQL-запрос

В настоящий момент я создаю простое веб-приложение, которое я открою с открытым исходным кодом. Поскольку он стоит на данный момент, навигатор генерируется при каждой загрузке страницы (которая будет меняться для кэширования в один прекрасный день), но на данный момент это делается с помощью кода ниже. Используя PHP 5.2.6 и MySQLi 5.0.7.7, насколько эффективнее может быть код ниже? Я думаю, что помощь может помочь, но я советую. Любые советы будут очень признательны.

<?php $navQuery = $mysqli->query("SELECT id,slug,name FROM categories WHERE live=1 ORDER BY name ASC") or die(mysqli_error($mysqli)); while($nav = $navQuery->fetch_object()) { echo '<li>'; echo '<a href="/'. $nav->slug .'">'. $nav->name .'</a>'; echo '<ul>'; $subNavQuery = $mysqli->query("SELECT id,name FROM snippets WHERE category='$nav->id' ORDER BY name ASC") or die(mysqli_error($mysqli)); while($subNav = $subNavQuery->fetch_object()) { echo '<li>'; echo '<a href="/'. $nav->slug .'/'. $subNav->name .'">'. $subNav->name .'</a>'; echo '</li>'; } echo '</ul>'; echo '</li>'; } ?> 

Вы можете запустить этот запрос:

 SELECT c.id AS cid, c.slug AS cslug, c.name AS cname, s.id AS sid, s.name AS sname FROM categories AS c LEFT JOIN snippets AS s ON s.category = c.id WHERE c.live=1 ORDER BY c.name, s.name 

Затем перейдите по результатам, чтобы создать правильный заголовок, например:

 // last category ID $lastcid = 0; while ($r = $navQuery->fetch_object ()) { if ($r->cid != $lastcid) { // new category // let's close the last open category (if any) if ($lastcid) printf ('</li></ul>'); // save current category $lastcid = $r->cid; // display category printf ('<li><a href="/%s">%s</a>', $r->cslug, $r->cname); // display first snippet printf ('<li><a href="/%s/%s">%s</a></li>', $r->cslug, $r->sname, $r->sname); } else { // category already processed, just display snippet // display snippet printf ('<li><a href="/%s/%s">%s</a></a>', $r->cslug, $r->sname, $r->sname); } } // let's close the last open category (if any) if ($lastcid) printf ('</li></ul>'); 

Обратите внимание, что я использовал printf но вместо этого вы должны использовать свою собственную функцию, которая обтекает printf, но запускает htmlspecialchars через параметры (кроме первого, конечно).

Отказ от ответственности: я не обязательно поощряю такое использование <ul> s.

Этот код приведен здесь, чтобы показать основную идею обработки иерархических данных, полученных одним запросом.

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

Что касается запроса, то yup-соединение может сделать это в 1 запросе.

 SELECT * -- Make sure you only select the fields you want. Might need to use aliases to avoid conflict FROM snippets S LEFT JOIN categiries C ON S.category = C.id WHERE live = 1 ORDER BY S.category, C.name 

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

Что-то вроде

 $categories = array(); foreach ($results as $result) { $snippet = array(); //assign all the snippet related data into this var if (isset($categories[$result['snippets.category']])) { $categories[$result['snippets.category']]['snippet'][] = $snippet; } else { $category = array(); //assign all the category related data into this var; $categories[$result['snippets.category']]['snippet'] = array($snippet); $categories[$result['snippets.category']]['category'] = $category; } } 

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

Я бы попробовал это:

 SELECT c.slug,c.name,s.name FROM categories c LEFT JOIN snippets s ON s.category = c.id WHERE live=1 ORDER BY c.name, s.name 

Я не тестировал его. Также проверьте индексы с помощью оператора EXPLAIN, чтобы MySQL не выполнял полное сканирование таблицы.

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

Помимо одного комбинированного запроса вы можете использовать два отдельных.

У вас есть базовая древовидная структура здесь с элементами ветвления (таблица категорий) и листовыми элементами (таблица фрагментов). Недостатком решения с одним запросом является то, что вы повторно получаете элемент brach-элемента владельца для каждого элемента листа. Это избыточная информация, и в зависимости от количества листов и объема информации, которую вы запрашиваете от каждого элемента ветви, можно получить большой объем дополнительного трафика.

Решение с двумя запросами выглядит так:

 $navQuery = $mysqli->query ("SELECT id, slug, name FROM categories WHERE live=1 ORDER BY name") or die (mysqli_error ($mysqli)); $subNavQuery = $mysqli->query ("SELECT c.id AS cid, s.id, s.name FROM categories AS c LEFT JOIN snippets AS s ON s.category=c.id WHERE c.live=1 ORDER BY c.name, s.name") or die (mysqli_error ($mysqli)); $sub = $subNavQuery->fetch_object (); // pre-reading one record while ($nav = $navQuery->fetch_object ()) { echo '<li>'; echo '<a href="/'. $nav->slug .'">'. $nav->name .'</a>'; echo '<ul>'; while ($sub->cid == $nav->id) { echo '<li>'; echo '<a href="/'. $nav->slug .'/'. $sub->name .'">'. $sub->name .'</a>'; echo '</li>'; $sub = $subNavQuery->fetch_object (); } echo '</ul>'; } 

Он должен полностью напечатать тот же код, что и ваш пример.

 $navQuery = $mysqli->query("SELECT t1.id AS cat_id,t1.slug,t1.name AS cat_name,t2.id,t2.name FROM categories AS t1 LEFT JOIN snippets AS t2 ON t1.id = t2.category WHERE t1.live=1 ORDER BY t1.name ASC, t2.name ASC") or die(mysqli_error($mysqli)); $current = false; while($nav = $navQuery->fetch_object()) { if ($current != $nav->cat_id) { if ($current) echo '</ul>'; echo '<a href="/'. $nav->slug .'">'. $nav->cat_name .'</a><ul>'; $current = $nav->cat_id; } if ($nav->id) { //check for empty category echo '<li><a href="/'. $nav->slug .'/'. $nav->name .'">'. $nav->name .'</a></li>'; } } //last category if ($current) echo '</ul>';