![]() |
Проблемы с построением "дерева"(+)
Решил тут написать каталог, простенький. Взял банальный алгоритм(не помню как он называется):
ID-PARENT_ID т.е. каждая ветка знает своего родителя. рекурсивной функцией строю дерево. решил провести тест. заполнил базу 10000 записями. причем, каждая ветка имеет 1 потомка. т.е. получается 10к запросов вида: select * from table where parentid = id apache падает(а может и не он). понимаю что в реальности такой алгоритм медленный и тяжелый, но мне просто нужно было проверить. что интересно, ASP.NET+IIS справляется, хотя и долго. собственно в чем могут быть грабли(я имею ввиду настройки php, apache, mysql)? p.s. про другие алгоритмы знаю, но некогда их реализовывать, да и для этой задачи - пустая трата времени. p.p.s. hard&soft: athlon xp1700+, 512 DDR winxp sp1 eng. apache 1.3.x php 4.3.9(как модуль). mysql 3.23.58 d-max-nt кстати 5-ый php, опять же - как модуль, "съел" - 6k записей, после чего вывод на страницу просто остановился. p.p.p.s. добавил принудителньое закрытие mysql соединения после каждого запроса. Похоже виновата все же mysql. Теперь под php4 также просто останавливается вывод. может кто подскажит утилиту(поудобнее), чтобы смотреть текущие connections и queries для mysql. к сожалению mysql administrator требует mysql 4.x. |
Может быть просто гдето в настройках прописано максимальное время выполнения. У меня както была похожая проблема. Увеличил время и всё заработало.
|
оно падает гораздо раньше, чем выходит время выполнения скрипты:)
|
Возможно, скрипту не хватает оперативки. По умолчанию там стоит 8M.
В любом случае, можно включить отображение ошибок либо поискать их в логе. |
ошибки у меня отображаются, логи тоже молчат по этому поводу. а что стоит 8 МБ? как параметр называется?
|
Тогда скрипт в студию :)
|
Цитата:
Цитата:
|
вот скрипт:
Код:
class Category Код:
function Tree($categoryParentID) Код:
class MySql |
проблема походу точно в mysql, т.к. опытным путем установил, что коннекта не происходит, видимо их слишком много, но что интересно:
mysql_err* пустые:) |
попробуй сделать выборку категорий только первого уровня.
Но, вообще, не стоит не каждый запрос делать новое подключение. Да и в других местах, есть где оптимизировать. |
Вложений: 1
это может помочь (класс для работы с деревьями любой вложенности)
|
мой класс проходит свободно 500 уровней вложенности. потом начинаются проблемы.
про nested sets я знаю. но не хочу его применять, т.к. в этом проекте он не нужен, а там много времени уйдет на реализацию... а что можно оптимизировать? мой интерес чисто теоритический, т.к. в реальных приложениях я ограничиваю вложенность до 100 категорий(на данном алгоритме). p.s. за файлик спасибо... |
zaartix
Спасибо за класс =) Как раз искал что-то подобное. |
2 Sheryld
Я сильно с алгоритмом не разбирался, но вот, что сразу броилось в глаза (оно большого прироста не даст, но так приятнее работать ;) ) Код: Код:
$this->categoryInfo['id'] = $row['cat_ID']; Код:
$this->categoryInfoCollection[] = $row; И, наконец, главное. Учитывая, что допустимы 100 уровней вложенности, я бы все-таки дважды подумал, прежде чем использовать рекурсию для дерева. IMHO Лишняя нагрузка серверу ни к чему. |
ну а как реализовать дерево на php без рекурсивного алгоритма, но с моей структурой?
|
Чтобы не заморачиваться, можно, например, ввести еще одну таблицу следующей структуры:
Код:
treeid INTEGER AUTOINCREMENT Используя такую таблицу можно легко получить любую ветку дерева одним запросом с условием LIKE. Не самый лучший способ, но будет на порядок эффективнее, чем рекурсия. |
такой алгоритм тоже знаю, но в принципе против него, т.к. нарушаются условия нормализации.
к тому же его следует применять, имхо, только если есть в наличии ms sql server 2K, например. чтобы повесить основные операции по поддержке дерева на триггеры. т.е. получить структуру саму себя поддерживающую. в принципе уже каркас написал. по первым тестам(искуственным) работает весьма шустро, если скрестить с кешем - то самое оно. ежели на реальном тесте производительность сильно ухудшится, то наверное возьму за основу nested sets. у меня там каталог полностью "кастомный", т.е. помимо менеджмента категорий там еще предполагается, что каждая единица в категории имеет неограниченное, настраиваемое число параметров определенных заранее типов(joins, joins, joins). |
Цитата:
Цитата:
Цитата:
Цитата:
Цитата:
|
nested sets конечно рулит:
~0.001 рекурсия: ~0.1 :) |
а как скажем сделать такую вещь(при таком классе nested sets): сортировка каждой ветки дерева но с сохранением порядка.
пример: должно быть: Код:
аа Код:
аа p.s. при рекурсивном алгоритме можно получить такой список сразу... |
запросом такую штуку не сделать.
p.s. и это только начало:) попробуйте на досуге сделать методы движения нодов вверх-вниз, полный путь и т.д. Цитата:
|
начал портировать класс dbtree для своих нужд. если кому интересно:
mysql helper class: Код:
<?php Код:
CREATE TABLE categories ( Код:
require_once $DOCUMENT_ROOT . "/sherman/classes/mysql.inc"; еще один метод(удаляет ветку): Код:
function deleteAll($categoryID) |
метод для нахождения родительской категории:
Код:
function getParent($categoryID, $level=1) |
метод переноса ветки(смена родителя).
Код:
function updateNode($categoryID, $newCategoryParentID, $nodeInfoData) |
найти весь путь от корня до категории:
Код:
function enumPath($categoryID, $showRoot=false) |
метод для проверки целостности таблицы дерева(не оптимизирован):
Код:
function checkTree() |
вобщем, резюмирую.
для реальных приложений база данных mysql версии 3.23.x(в моем случае еще и без innodb) подходит очень плохо. сейчас почти доделал каталог: неограниченная вложенность каждая категория имеет набор полностью настраиваемых параметров(несколько типов). каждая категория может иметь неограниченное число позиций(с параметрами). плюс еще работа с фотографиями(они могут быть привязаны к категории или позиции, причем каждая картинка может быть в двух вариантах: большая и маленькая). т.е. почти везде идет отношение «многие ко многим». а в mysql этой версии(впрочем и в 4.x) невозможно создать струкутру, которая будет поддерживать сама себя. рассмотрим сценарий удаления категории: удаляется категория. удаляются все подкатегории(т.е. вся ветка). удаляются все связи параметров, относящиеся к удаленным категориям). удаляются параметры, которые не были закреплены ни за одной категорией из оставшихся не удаленных. удаляются все позиции, которые относились к удаленным категориям. удаляются все значения параметров, которые были связаны с позициями, которые в свою очередь относились к удаленным категориям. удаляются все картинки(записи в бд+файлы) относящиеся к удаленным категориям и позициям. если бы имелась в наличии нормальная СУБД, то многие вещи можно было бы сделать автоматически(каскадное удаление+триггеры). а теперь прикиньте код на php, и причем если что-то где-то пойдет не так, то транзакции нету:) в итоге. mysql слабо подходит для более менее серьезных вещей. мое имхо. больше не возьмусь делать такие вещи на мускуле, пусть заказчик бошку чешет, насчет хостинга:) кроме того, объем кода, обслуживающий всю эту систему уже сотни килобайт:) не считая административного интерфейса и вывода:) p.s. а теперь представьте, что происходит при смене родительской категории:) p.p.s. а многие вещи, которые задумывались, мне не удалось реализовать в принципе:( ======================================================== Кстати вот еще вопрос. Заказчик хочет, чтобы пути выглядели так: /catalog/phone/nokia/ /catalog/phone/nokia/7800/ - последнее это уже позиция а не категория /catalog/food/fastfood/mcdonalds/ /catalog/food/fastfood/mcdonalds/gamburger/ - последнее это уже позиция а не категория Написал pathFinder. алгоритм(только для категорий): всю цепочку загоняем в массив. находим последний элемент в цепочке. по имени последнего элемента, получаем id(их может быть несколько, т.к. может быть вот так: /catalog/kpk/rover/ /catalog/phone/rover/ далее находим все пути и в цикле сравниваем, как только нахоим нужный путь, выходим. вопрос: можно ли сделать что-то с помощью mode_rewrite? или может выгоднее завести таблицу aliases и там хранить все привязки, т.к. находить id категории и сразу проверять? к сожалению такой вариант: category.html?catid=id не возможен:( |
Цитата:
|
ну методы доступа мягко говоря не отпимизированы. большинство из них можно свести к двум, максимум трем запросам. Не так давно я этим и занимался. Например поиск пути это всего один запрос... вывод детей - тоже... ну и так далее. Если будет желание - обсудим.
Интересно послушать, какие задачи не реализуются в такой идеологии... |
а там и есть один запрос:
enumPath, enumChildren. интересно было бы услышать про сортировку. т.е. как реализовать вывод полностью остортированного дерева, или лучше сразу вставлять категорию на «нужное место»? тогда нужно менять алгоритм по-идее. про оптимизацию запросов тоже интересно. я написал, что задачи не реализуются не в идеологии, а в конкретных инстурментах. p.s. Celko в своей статье про вложенные множества в sql вовсю пользуется хранимыми процеурами, подзапросами, триггерами и т.д. у нас же(mysql) много до сих пор так и нет:( я просто написал свои впечатления о использовании этой модели в реальном приложении, на конкретных инструментах. p.p.s. версия mysql 3.23.58. |
Цитата:
|
не. попробуй вывести такое дерево в алфавитной сортировке:)
типа: Код:
аа 1. сортировать при выводе. 2. вставлять в «правильную позицию» сразу. |
это проблема реализации. как правило всегда при выводе списков нужно иметь несколько вариантов сортировке.
решение 1. оставить на совести менеджера контента решение 2. написать небольшой класс сортировщик. и наследовать от него по мере надобности новые види сортировок. при этом у менеджера будет кнопочка (ссылочка, выпадающий список и т.п.) которая и будет это делать. имхо полезней второе. |
а не подскажешь алгоритм сортировки моего списка:
1 узел дерева, сортировка нужна по nodeItem["data"][desc"]: Код:
var $nodeInfo = array( массив узлов дерева. можно конечно ввести еще одно поле parentID и таким образом поддерживать две структуры(эту и классическую). и там уже сортировку сделать достаточно легко(например, рекурсией). а вот как быть тут — хз. |
в твоем случае очень удобно сортировать по полю left. Оно по построению дерева является индексом сортировки. вот его и нужно изменять.
Ну а дальше хоть пузырьком, хоть бинарным ) Базовый алгоритм (который собственно сортирует) один и тот же всегда. Определись, какой для тебя лучше подходит (в списке альтернатив есть и usort() :-)). Метод сравнения - в зависимости от типа сортировки. |
по left у меня и так выводится. но если отстортировать все после по desc, то нарушается структура. пока что решения не придумал :(
все что у меня получилось это отсортировать ноды на каждом уровне, но собрать их снова в дерево не получается. |
можно еще как вариант, просатвить сначала всех родителей, а потом сразу выводить рекусривной функцией. но это решение в лоб. думаю попозже все-таки реализую функцию вставки ветви на в конкретное место, чтобы хранить дерево уже отсортированным.
решение: Код:
$catObj = new Category2(); |
Цитата:
Понятно что это довольно утомительный процесс.. хотя после некоторых размышлений, можно увидеть, что достаточно будет N запросов при сортировке. можно конечно один с кучей if-ов, но на мой взгляд это будет медленней. Теперь второй момент. кто тебе мешает при выводе дерева пересортировать его как тебе нужно? тогда не нужно будет никаких доп. запросов. но в этом случае пропадает т.н. обобщенность, то есть нужно будет писать кучу классов-сортировщиков (для каждой отдельной сортировки)... но не будет большого количества запросов. |
в моем случае целесообразнее хранить отсортированное дерево, т.к. запросов на добавление и изменение категорий в разы меньше, чем запросов на вывод.
про left и right понятно:) но алгоритм пока еще мне в голову не пришел. в моем примере сортировка организована как раз после запроса. т.е. сначала идет выборка всего дерева, отсортированного по left. затем идет простановка цепочки parent-child. и затем уже на каждом уровне идет сортировка(рекурсия) по алфавиту. имхо, если делать возможность хранения отсортированного дерева по n-полям(т.е. по любому полю), тогда нужно просто завести отдельную таблицу(или даже n-таблиц) и в ней хранить порядок. а запросы будут вида: Код:
select t.* from myOrderTable as o inner join myTreeTable as t in |
1. order by left, o.order (иначе стуктура слетит)
2. а если нужно по нескольким полям сортировать? |
Часовой пояс GMT +4, время: 14:01. |
Powered by vBulletin® Version 3.8.5
Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.