Строим дерево (флейм)

12
Коля Дубр
На сайте с 02.03.2005
Offline
153
5155

Флеймово-провокационный топик. Предыстория:

AndreM:
сегодня ему показали инсерты в цикле, а завтра он додумается рекурсивное дерево в цикле с запросами строить.
Коля Дубр:
Ну и напоследок. Вывод дерева через рекурсию - вполне имеет право на существование, а во многих случаях так и вовсе оптимален. Попробуйте убедить в обратном :)
AndreM:
3. Дерево через рекурсию и должно быть, не должно быть запроса в рекурсивной функции. В этом Вас переубеждать не надо?

А почему бы и нет? Для начала давайте определимся со способом хранения. Вы какой предпочитаете? :)

Разрабатываю общую шину (http://habrahabr.ru/company/floxim/blog/268467/) помаленьку. ...а еще у меня есть бложек (http://www.blogovo.ru/).
AM
На сайте с 12.09.2007
Offline
47
#1

Итак.

Условия: есть 1000 категорий в виде дерева, уровни вложения не ограничены. Хранится в базе, стуктура стандартная id, name, id_parent. Связанные списки и тд не трогаем, так как структуру менять нельзя, и иначе все рухнет.

Необходимо:

- получить полное дерево для селекта поиска;

- путь до текущей категории;

- раскрытое меню до текущей категории;

- доступ к названию любой категории;

Ваши предложения. Поехали ))

С уважением, Морозов Андрей, разработчик проекта eTXT.ru (http://www.etxt.ru/?r=morozov), icq 55377667
S
На сайте с 15.07.2008
Offline
30
#2
AndreM:
Итак.
Условия: есть 1000 категорий в виде дерева, уровни вложения не ограничены. Хранится в базе, стуктура стандартная id, name, id_parent. Связанные списки и тд не трогаем, так как структуру менять нельзя, и иначе все рухнет.
Необходимо:
- получить полное дерево для селекта поиска;
- путь до текущей категории;
- раскрытое меню до текущей категории;
- доступ к названию любой категории;

Ваши предложения. Поехали ))

А какие тут могут быть предложения? Stored procedures - это наше всё.

Банки Украины (http://www.bankstore.com.ua) Генератор сайтмепов (/ru/forum/272468) Ода Гугльботу (/ru/forum/285758)
Коля Дубр
На сайте с 02.03.2005
Offline
153
#3

Ну, формулируя задачу "как в учебнике" Вы рискуете нарваться на копипаст ответа из учебника, а это неспортивно :)

Так что я кое-что уточню, а Вы поправьте. Во-первых, ограничим вложенность 5 уровнями (больше редко бывает, по крайней мере на 1000 категорий). Во-вторых, предположим относительно равномерное распределение подкатегорий.

AndreM:
Связанные списки и тд не трогаем

Так вроде структура, которую Вы описали - и есть связанные списки (списки смежности, Adjacency List).

AndreM:
- получить полное дерево для селекта поиска;

Полное дерево получается запросом "SELECT * FROM tree", и далее какой-то такой ахтунг. Про "селект поиска" не понял.

AndreM:
путь до текущей категории;

Выбираем текущую запись, добавляем в массив. Если родитель - не корень, выбираем родителя, добавляем в массив. Если не корень... и т.д.

Как Вы думаете, что эффективней: 5 запросов по primary key, или 5 джойнов таблицы на саму себя? А что доступней для понимания?

AndreM:
- раскрытое меню до текущей категории;

Получаем путь, далее идем по уровням, начиная с корня, если узел есть в пути - уходим в рекурсию. Соответственно, еще 5 очень легких запросов.

AndreM:
доступ к названию любой категории;

Не понял. SELECT name FROM tree WHERE id = 22?

Критикуйте :) Вариант с 1.500.000 записей не предлагать. Сами сказали, что их тыща :)

Про "индусский стиль" ничего не хочу слышать.

Santyago:
Stored procedures - это наше всё.

А у нас mysql4 :(

Bazis007
На сайте с 10.06.2008
Offline
84
#4

Если дерево категорий редко обновляется, то долбим селектами с последующим кешированием результата, при первом запросе ветки. Потом всё берём из кеша. :)

При редактировании категории кеш убивается и формируется заново.

При переносе веток - высчитываем все ветки, которые затронет данное изменение и убиваим их кеш.

timur-kar
На сайте с 29.05.2006
Offline
85
#5
Коля Дубр:
Критикуйте :) Вариант с 1.500.000 записей не предлагать. Сами сказали, что их тыща :)
Про "индусский стиль" ничего не хочу слышать.

В дереве одна из распространенных задач "искать в ветке" (искать в категории и подкатегориях) или например сосчитать рекурсивно количество элементов (которые могут часто меняться, так что кешированием здесь не спастись) в ветке, при таком типе хранения как предложите это делать ?

своего варианта решения не предлагаю, просто не могу сказать что в деревьях что-то понимаю

AM
На сайте с 12.09.2007
Offline
47
#6

1. Задача не из учебника, а реальная. Проект http://www.tkat.ru

2. Связанные списки, я не математик, поэтому понимаю, что в этом случае каждая категория знает соседа и тд. Без доп выборок. Ну это оффтоп в нашем вопросе.

3. Селект - это я к тому, зачем на странице полное дерево. Например, искать в категории. А то скажут, что не нужно.

4. По большому счету и по моему мнению уровень вложенности не должен играть какую либо важную роль.

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

6. Практика показала, что доставание категорий запросом select * from category и последующий разбор пхпой, с возможным кэшированием на диске/в памяти построенного дерева, эффективней рекурсивных запросов примерно в 1000 раз - только по замеру времени, а про зависшие эти запросы в процессах под реальной нагрузкой я уже забыл. 🚬

Коля Дубр
На сайте с 02.03.2005
Offline
153
#7

timur-kar, для таких вещей как раз придумали Nested Sets. На такой структуре, очевидно, придется найти все айдишники подветвей и делать WHERE parent_id IN (1, 2, 3...). Подветви выбираем либо по схеме "раскрытое меню до текущей категории", либо последовательностью запросов с WHERE parent_id IN (...) по числу уровней. Но это действительно тяжелая штука. Я в итоге все-таки добавил nested sets специально под это.

AM
На сайте с 12.09.2007
Offline
47
#8
Коля Дубр:
timur-kar, для таких вещей как раз придумали Nested Sets. На такой структуре, очевидно, придется найти все айдишники подветвей и делать WHERE parent_id IN (1, 2, 3...). Подветви выбираем либо по схеме "раскрытое меню до текущей категории", либо последовательностью запросов с WHERE parent_id IN (...) по числу уровней. Но это действительно тяжелая штука. Я в итоге все-таки добавил nested sets специально под это.

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

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

А про доступ к каждой категории. Предположим есть список статей с категоризацией. Если мы уже построили дерево для поиска в категориях, неужели мы будет опять выбирать названия категории, к которой относится каждая статья?

timur-kar
На сайте с 29.05.2006
Offline
85
#9
Коля Дубр:
timur-karЯ в итоге все-таки добавил nested sets специально под это.

Я правильно понимаю что у вас nested sets "поверх" первоначальной структуры ? т.е. дополнительные поля в БД, которые не определяют иерархию а просто копируют её в этом виде

Коля Дубр
На сайте с 02.03.2005
Offline
153
#10

AndreM, а, вот тут-то мы и добрались до самого интересного, бишь до кеширования. Тогда внимание вопрос: если мы кэшируем дерево целиком, нам не пофигу, как его выбирать? Хоть тыщей запросов :) И еще вопрос: а зачем кэшировать промежуточно-абстрактное "дерево целиком", если можно закешировать результаты?

Насчет зависающих процессов - это надо смотреть на Вашу конкретную систему. Убейте, не пойму, где должен повиснуть запрос, достающий 1 запись по primary :)

ОК, а как Вы решаете задачу поиска в категории?

timur-kar, да, именно так. Ну, с небольшими хитростями.

12

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