Написал код, который составляет прайс-лист товаров сгруппированный по категориям. Вложенность категорий - произвольная. Категории имеют парентов. Если категория корневая, то у неё парент равен нулю. Обычная древовидная структура. В каждой из категорий, кроме имеющей id=0, могут содержатся товары. Вот собственно код: PHP: $result = mysql_query("SELECT categoryID, name FROM `SS_categories` WHERE parent = 0 ORDER BY name"); while ( $cats = mysql_fetch_row($result) ){ /* название текущей категории */ print "<tr><td>$cats[1]</td></tr>\n"; /* перечисление товаров в текущей категории */ $roots = mysql_query("SELECT categoryID, productID, name, price, count FROM `SS_products` WHERE categoryID = $cats[0] AND enabled = 1 ORDER BY name"); while ( $rootProds = mysql_fetch_row($roots) ){ /* вывод названия товара, цены и его наличия */ if ( $rootProds[4] ) $cnt = "+"; else $cnt = "-"; print "<tr>\n"; print "<td>$rootProds[2]</td>\n"; print "<td>$rootProds[3]</td>\n"; print "<td>$cnt</td>\n"; print "</tr>\n"; } /* получение всего списка товаров */ $res = mysql_query("SELECT categoryID, productID, name, price, count FROM `SS_products` WHERE enabled = 1 ORDER BY name"); while ( $prods = mysql_fetch_row($res) ){ /* проверка принадлежности товаров к текущей категории */ if ( is_embedded($cats[0], $prods[0]) ){ if ( $prods[4] ) $cnt = "+"; else $cnt = "-"; print "<tr>\n"; print "<td>$prods[2]</td>\n"; print "<td>$prods[3]</td>\n"; print "<td>$cnt</td>\n"; print "</tr>\n"; } } } /* ф-ция определения принадлежности подкатегории child категории root */ function is_embedded ($root, $child){ $r = mysql_query("SELECT parent FROM `SS_categories` WHERE categoryID = $child") or die(mysql_error()); if ( $row = mysql_fetch_row($r) ){ if ( $root==$row[0] ) return true; if ( 0 == $row[0] ) return false; return is_embedded ($root, $row[0]); } } Код работает безошибочно. Но есть одна проблема. Состоит в следующем: Тестировал на трех серверах: hoster.ru, jino-net.ru и localhost(denver). На первом хостинге код тормозит безбожно. Время выполнения составляет в лучшем случае 5 сек. В худшем - генерация страницы прерывается с сообщением об ошибке превышения тайм-лимита (30 сек). На втором и третьем серверах код отрабатывает за время менее 0.2сек. Сама база маленькая: количество категорий в таблице - 38, количество товаров - тоже 38. Могут ли быть здесь в коде какие-то проблемы, которые вкупе с характеристиками хостера дают такой результат? Я вижу одну неоптимальность, связаную с выполнением в цикле запроса по получению всего списка товаров, но способа оптимизировать это не вижу. Связаны ли тормоза при загрузке с hoster.ru с неоптимальностью кода, или это уже проблемы с хостингом? P.S. Мне кажется, что проблема именно в php-составляющей, а не в mysql, т.к. если я захожу на сервер из утилиты mysql.exe, то никаких тормозов с запросами нет. Спасибо.
ShopScript ? Который на Smarty. Это весь код на странице или только твой, обёрнутый в шаблон смарти? Это я к тому спрашиваю, что - уверен ли ты, что именно твой кусок тормозит? Если уверен - тогда это, скорее всего, проблемы с хостингом. Как вариант - попробуй померять, какой именно кусок тормозит. PHP: <? $time_start = microtime(1); // Кусок кода $time_end = microtime(1); $time = $time_end - $time_start; echo "Кусок кода работал $time секунд\n"; ?>
Да тут и так видно, чего тормозит. Выбираешь все! данные из таблицы, да ещё и сортируешь их по текстовому полю. Потом для каждого! продукта ты делаешь ещё один запрос А ты посчитай сколько запросов на странице выходит? Решение одно. Перепиши код заного.
Нет, это не shop script, и не smarty. Код полностью свой, никаких шаблонов. Структура БД чем-то напоминает ShopScript, но не повторяет полностью. Измерял время работы именно этого фрагмента (который был приведен). А что делать? мне нужно определять принадлежность каждого товара к каждой категории, чтобы принять решение о включении этого товара в список категории. Можно попробовать список товаров получить один раз и сохранить в массиве, а потом вместо запроса проходить по массиву. Это что касается запросов в цикле. По запросам в функции is_embedded, выполняющимся рекурсивно: Как отследить принадлежность (вложенность) одной категории к другой без рекурсии? Если бы список категорий был бы не древовидный, а плоский, никаких проблем не было бы. А отказаться от древовидного списка я не могу.
Организация дерева предствлена тут: http://hooliganos.jino-net.ru/sitemap.php Как выглядит список, можно посмотреть тут: http://hooliganos.jino-net.ru/price.php Только надо залогинится с этими данными: пользователь: Владимир пароль: 7f0e6c
Хулиган Спасибо за информацию. Мое решение имеет следующий вид: 1. Достаточно выполнить один SQL запрос ("код, который составляет прайс-лист товаров"), т.к. у нас нет ограничения на категории (кроме parent = 0) 2. Обработать данные. Нужно получить массив вида PHP: <?php $category = array( 0 => array( 'Автомобили' => array( 'Opel' => true, 'Porshe' => true, 'Mercedes' => true ), 'Автоэлектроника' => array( 'Блоки управления зажиганием' => array( 'ВАЗ' => true, 'ГАЗ' => true ) ) ) ); 3. Затем выбрать все продукты (SELECT * FROM product) 4. Выполнить цикл по всем продуктам Итого: два SQL запроса, один блок формирования дерева, один - формирования товаров PS. Ассоциативнй массив $category можно представить в виде обыкновенного массива, где ключи будут строится исходя из category_id
Может я не совсем понял идею, но мне кажется, что для построения такого массива мне никуда не деться от рекурсии, аналогичной рекурсивной ф-ции is_embedded. Фактически я избавлюсь от множественных вызовов ф-ции is_embedded, заменив их однократной рекурсией по составлению массива. Но зато потом я вынужден буду ввести ещё одну рекурсию по обходу построенного массива. Т.е. предлагается замена 38 маленьких рекурсий на две больших (одна по составлению массива, вторая по его анализу). Или это не так?
нет, не так, на первый взгляд все обходится вообще без рекурсий ... использующих sql-запросы Тут самое сложное: алгоритм построения дерева. Если Вам нужно достать единичную категорию, например "Opel", то действительно, можно будет использовать рекурсию, при этом одну рекурсию... is_embedded() Но если Вы делаете полный прайс лист ( о чем собственно говорится в задании ), то необходимость в is_embedded() отпадает Попробуйте развить мою мысль и задаться вопросами: А можем ли мы в каждом узле хранить информацию о родительском дереве? А можем ли мы хранить информацию родительского id в БД? А можем ли мы построить дерево таким образом, чтобы поиск по дереву был максимально прост.
Да, пожалуй хранение родительского id в базе - это хорошая идея. Дерево целиком мне не нужно, достаточно знать id самого старшего предка данного узла. Можно добавить в таблицу SS_categories поле, содержащее id самого старшего узла (у которого парент=0). Необходимость в is_embedded исчезает. Исчезает также необходимость в построении таких жутких массивов (чего я очень боюсь) При этом нужно при добавлении новой категории в БД вычислить корневой узел, что будет делаться один раз и проблем со временем никаких. Попробую сделать так и посмотрю на скорость выполнения. P.S. наверное даже лучше корневой id прописать не категории, а товару. При этом делается один запрос на все товары order by major_id И всё.
насчет дерева... сдесь же на форуме наткнулся на любопытную ссылку, рекомендую ознакомиться... http://getinfo.ru/article610.html
Очень даже недурственно получилось. Каждому продукту указал его корневую категорию majorID. Более чем 1800 запросов к БД свелись к 13 запросам (один на построение списка корневых + по одному запросу для каждого корня): PHP: <? php $roots_arr = mysql_query("SELECT categoryID, name FROM `SS_categories` WHERE parent = 0 ORDER BY name"); while ( $root = mysql_fetch_row($roots_arr) ){ print "<tr><td>$root[1]</td></tr>\n"; $prods_arr = mysql_query("SELECT categoryID, productID, name, price, count FROM `SS_products` WHERE majorID = $root[0] AND enabled = 1 ORDER BY name"); while ( $prods = mysql_fetch_row($prods_arr) ){ if ( $prods[4] ) $cnt = "+"; else $cnt = "-"; print "<tr>\n"; print "<td>$prods[2]</td>\n"; print "<td>$prods[3]</td>\n"; print "<td>$cnt</td>\n"; print "</tr>\n"; } } ?> Скорость возросла в 7-8 раз. Чем больше глубина вложений (и рекурсий соответсвенно) тем значительней будет выигрыш. Правда, пришлось несколько переделать систему управления расположением категорий и продуктов, но это уже не критично. Даже не знаю, стоит ли таскать всё дерево? Делать отдельную таблицу с деревьями (лесопарк)? Блин, таблиц будет, как у собаки блох... Ссылка от Xerk очень даже интересная, но это провоцирует на то, чтобы переделывать всю структуру базы и сайта. Я сейчас не готов к такому шагу В общем, все очень даже неплохо получилось. Спасибо всем!