Неэффективный 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>';
}
?>
Ответы
Ответ 1
Вы можете запустить этот запрос:
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 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 close the last open category (if any)
if ($lastcid)
printf ('</li></ul>');
Обратите внимание, что я использовал printf
, но вместо этого вы должны использовать свою собственную функцию, которая обертывается вокруг printf, но запускает htmlspecialchars
через параметры (кроме первого, конечно).
Отказ от ответственности: я не обязательно поощряю такое использование <ul>
s.
Этот код только здесь, чтобы показать основную идею обработки иерархических данных, полученных с одним запросом.
Ответ 2
Во-первых, вы не должны запрашивать свою базу данных в своем представлении. Это будет смешивать вашу бизнес-логику и логику представления. Просто присвойте результаты запроса переменной в контроллере и проведите через нее.
Что касается запроса, то 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;
}
}
Это должно дать вам массив категорий, которые имеют все связанные фрагменты в массиве. Вы можете просто пропустить этот массив, чтобы воспроизвести список.
Ответ 3
Я бы попробовал следующее:
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 и проверить, когда изменится название категории, и построить свой вывод по своему усмотрению.
Ответ 4
Помимо одного комбинированного запроса вы можете использовать два отдельных.
У вас есть базовая древовидная структура с элементами ветвления (таблица категорий) и элементами листа (таблица фрагментов). Недостатком решения с одним запросом является то, что вы повторно получаете элемент 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>';
}
Ответ 5
Он должен полностью напечатать тот же код, что и ваш пример
$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>';