powered by simpleCommunicator - 2.0.60     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Готовое решение для работы с деревьями.
25 сообщений из 41, страница 1 из 2
Готовое решение для работы с деревьями.
    #33233975
Paul Chabinsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
Пожалуйста помогите проверить и доработать.
Очень нужна обработка ошибок толковая.
Замечания:
	1) после создания таблицы дерева в нее нужно вставить строчку корневого узла
	2) корневой узел может быть только один
	3) параметры x и y в процедурах вставки и перемещения обозначают
		x - слева или справа
		y - равносильный или подчиненный
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
CREATE SCHEMA "core" AUTHORIZATION "root";

CREATE TABLE "core"."tree" (
  "id" SERIAL, 
  "pid" INTEGER, 
  "lft" INTEGER NOT NULL, 
  "rgt" INTEGER NOT NULL, 
  "lvl" SMALLINT NOT NULL, 
  CONSTRAINT "PK_tree" PRIMARY KEY("id")
) WITHOUT OIDS;

CREATE INDEX "IX_tree" ON "core"."tree"
  USING btree ("lft", "rgt", "lvl");

CREATE OR REPLACE FUNCTION "core"."spTreePoint" (did integer, x boolean, y boolean) RETURNS "pg_catalog"."record" AS
$$
declare
	dr record;
	rr record;
begin
	select into dr id, pid, lft, rgt, lvl
		from "core"."tree" where id = did;
	if y then select into rr
		cast(dr.id as integer) as pid,
		cast(case when x then dr.rgt -  1  else dr.lft end as integer) as pnt,
		cast(dr.lvl +  1  as smallint) as lvl;
	else select into rr
		cast(dr.pid as integer) as pid,
		cast(case when x then dr.rgt else dr.lft -  1  end as integer) as pnt,
		cast(dr.lvl as smallint) as lvl;
	end if;
	return rr;
end;
$$
LANGUAGE 'plpgsql' VOLATILE CALLED ON NULL INPUT SECURITY INVOKER;

CREATE OR REPLACE FUNCTION "core"."spTreeInsert" (did integer, x boolean, y boolean) RETURNS integer AS
$$
declare
	rr record;
	nid int4;
begin
	select into rr pid, pnt, lvl from "core"."spTreePoint" (did, x, y)
		as (pid integer, pnt integer, lvl smallint);
	update "core"."tree" set rgt = rgt +  2 , lft = case when lft > rr.pnt then lft +  2  else lft end
		where rgt > rr.pnt;
	insert into "core"."tree" (pid, lft, rgt, lvl)
		values (rr.pid, rr.pnt +  1 , rr.pnt +  2 , rr.lvl);
	select into nid
		currval('core.tree_id_seq');
	return (nid);
end;
$$
LANGUAGE 'plpgsql' VOLATILE CALLED ON NULL INPUT SECURITY INVOKER;

CREATE OR REPLACE FUNCTION "core"."spTreeMove" (sid integer, did integer, x boolean, y boolean) RETURNS integer AS
$$
declare
	sr record;
	rr record;
	d_src integer;
	d_mov integer;
	d_lvl smallint;
begin
	select into sr lft, rgt, lvl from "core"."tree"
		where id = sid;
	select into rr pid, pnt, lvl from "core"."spTreePoint" (did, x, y)
		as (pid integer, pnt integer, lvl smallint);
	d_src := sr.rgt - sr.lft +  1 ;
	d_lvl := rr.lvl - sr.lvl;
	if rr.pnt > sr.rgt then
		d_mov := rr.pnt - sr.lft +  1  - d_src;
		update "core"."tree" set
			lft = case when rgt <= sr.rgt then lft + d_mov else case when lft > sr.rgt then lft - d_src else lft end end,
			lvl = case when rgt <= sr.rgt then lvl + d_lvl else lvl end,
			rgt = case when rgt <= sr.rgt then rgt + d_mov else case when rgt <= rr.pnt then rgt - d_src else rgt end end
		where rgt > sr.lft and lft <=  rr.pnt;
	else
		d_mov := rr.pnt - sr.lft +  1 ;
		update "core"."tree" set
			rgt = case when lft >= sr.lft then rgt + d_mov else case when rgt < sr.lft then rgt + d_src else rgt end end,
			lvl = case when lft >= sr.lft then lvl + d_lvl else lvl end,
			lft = case when lft >= sr.lft then lft + d_mov else case when lft > rr.pnt then lft + d_src else lft end end
		where rgt > rr.pnt and lft < sr.rgt;
	end if;
	update "core"."tree" set
		pid = rr.pid where id = sid;
	return  0 ;
end;
$$
LANGUAGE 'plpgsql' VOLATILE CALLED ON NULL INPUT SECURITY INVOKER;
...
Рейтинг: 0 / 0
Готовое решение для работы с деревьями.
    #33234218
Paul Chabinsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Код: plaintext
1.
2.
3.
4.
5.
Не знаю может я всех запутаю еще больше.
Вот  источник  основываясь на который я делал вышенаписанное.
Хотелось бы добавить еще несколько ссылок:
1.  Storing Hierarchical Data in a Database 
2.  Дерево каталогов NESTED SETS (вложенные множества) и управление им. Часть 2 
...
Рейтинг: 0 / 0
Готовое решение для работы с деревьями.
    #33236971
.-.-.-.-.-
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
я с процедурами постгреса знаком понаслышке, но ИМХО - это очень мало.

Могу посоветовать запостить это в
http://www.livejournal.com/userinfo.bml?user=ru_pgsql
http://www.livejournal.com/userinfo.bml?user=ru_php
http://www.livejournal.com/userinfo.bml?user=ru_webdev

эти сообщества читают некоторые люди, интересующиейся nested sets
...
Рейтинг: 0 / 0
Готовое решение для работы с деревьями.
    #33236991
Paul Chabinsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Код: plaintext
1.
2.
3.
4.
Эти процедуры для _управления_ деревом, на основе Nested Sets.
И единственное чего здесь не хватает это удаления узла :)
Предпологается что запросы на выборку построить сможет любой самостоятельно :)
За наводку спасибо, только вот у меня нет аккаунта в ЖЖ и я не знаю как этим добром пользоваться :(
...
Рейтинг: 0 / 0
Готовое решение для работы с деревьями.
    #33237022
Paul Chabinsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
Я вот счас сижу и думаю.
- А может все, как Guest, смотрят что мало процедур и думаю что это все ерунда?
Давайте немного поясню (ненавижу длинные посты):
Везде где я видел реализацию NS, функции вставки узла например, разбиты на несколько:
Слева на одном уровне
Справа на одном уровне
Слева на уровень ниже
...
То же самое с движением узла.
Причем практически во всех примерах которые я смотрел, половина вещей сделанна половина нет.
Посмотрев на это пришел к выводу, что для вставки и перемещения требуются два дополнительных параметра, которые помогут более точно задать точку назначения.
Я назвал их X и Y они оба логические (2 во второй степени = 4 хихи).
Получается что у меня четыре процедуры на вставку, и четыре процедуры на передвижение + процедура которая обслуживает координаты, и того 9.
Нужно всего лишь добавить два варианта удаления (вырезание узла, удаление с потомками). Это еще плюс две операции (но одна процедура).
...
Рейтинг: 0 / 0
Готовое решение для работы с деревьями.
    #33237399
nostromo
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
авторПосмотрев на это пришел к выводу, что для вставки и перемещения требуются два дополнительных параметра, которые помогут более точно задать точку назначения.
Я назвал их X и Y они оба логические (2 во второй степени = 4 хихи).

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

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

Еще один вопрос: могут ли быть пробелы в нумерации левых и правых ключей или это принципиальное условие?

P.S. Простите за обилие вопросов, это не со зла :) Мне действительно интересна эта тема.
...
Рейтинг: 0 / 0
Готовое решение для работы с деревьями.
    #33237428
Paul Chabinsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Код: plaintext
1.
nostromo
Все очень интересно, но не могли бы Вы пояснить (хотя бы на пальцах) смысл введенных логических переменных?
Что значит "более точно задать точку назначения"? Это оптимизирует поиск узла?


Они не улучшают время выполнения.
Они облегчают жизнь.

Исходное дерево:

Корень
Птицы
Звери
Лев
Растения

Вставка (Звери, X = 0, Y = 0) - _до_ Зверей на _том_же_уровне_

Корень
Птицы
НОВЫЙ
Звери
Лев
Растения

Вставка (Звери, X = 0, Y = 1) - _до_ _дочерних_ элементов Зверей

Корень
Птицы
Звери
НОВЫЙ
Лев
Растения

Вставка (Звери, X = 1, Y = 0) - _после_ Зверей на _том_же_уровне_

Корень
Птицы
Звери
Лев
НОВЫЙ
Растения

Вставка (Звери, X = 1, Y = 1) - _после_ _дочерних_ элементов Зверей

Корень
Птицы
Звери
Лев
Новый
Растения

Видите зависимость между значениями X,Y и подчеркнутыми фразами?
...
Рейтинг: 0 / 0
Готовое решение для работы с деревьями.
    #33237431
Paul Chabinsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
nostromo
Похоже, вложенные множества сильно оптимизируют скорость выборки из дерева, однако как зависит скорость вставки и удаления от числа узлов дерева? Эта зависимость сильно хуже линейной?


Насчет скорости обновления все надо проверять.
Точных данных пока не могу сообщить.

nostromo
Еще один вопрос: могут ли быть пробелы в нумерации левых и правых ключей или это принципиальное условие?


Мой вариант не допускает пробелов по идее ;)
Пробелы мешают быстро считать кол-во дочерних элементов вот таким запросом:

Код: plaintext
SELECT (rgt-lft- 1 )/ 2  as childs_count FROM tree_table

В результате этого запроса вы получите кол-во детей для каждого узла.
...
Рейтинг: 0 / 0
Готовое решение для работы с деревьями.
    #33237448
nostromo
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Спасибо. Функционально назначение переменных X и Y понятно.

Однако, с точки зрения употредления деревьев понятие "естественного порядка дочерних элементов", как правило, не важно. Например, вложенные файлы и папки мы сортируем каким-то внешним условием: по именам в алфавитном порядке, по дате и т.д. В редких случаях можно признать полезность естественного порядка, но его всегда можно реализовать как дополнительное поле узла дерева.

Вопрос про эффективность Вашего решения я задал потому, что много слышал о соответствующих проблемах реализации иерархических структур поверх реляционных БД.
Вопрос о пробелах в нумерации ключей для обхода связан с этим же: с ростом дерева расходы на обновление ключей при вставке и удалении узлов очевидно будут расти. Если разрешить пробелы в нумерации, то, как мне кажется, можно будет большинство вставок и удалений узлов без обшироных обновлений ключей. Когда-то давно строки программы на Бейсике нумеровались числами, кратными 10, чтобы удобнее было вставлять новые в случае необходимости.

Теперь по-поводу легкого подсчета количества дочерних элементов. Судить о том, что важнее -- эффективность модификаций дерева или легкость подсчета числа его элементов можно, на мой взгляд только исходя из конкретики прикладной задачи и результатов контрольных тестов на данных, близких к реальным.
Можно конечно придумывать лекарство, а потом искать болезнь, но в такой сугубо прикладной области, как базы данных, как мне кажется, более естественен обратный путь.
...
Рейтинг: 0 / 0
Готовое решение для работы с деревьями.
    #33238191
Paul Chabinsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Nostromo, если честно, не хочется полемизировать.
Данное решение было сделанно мной под конкретную задачу, такая задача стоит перед многими. В моем случае это создание удобной навигации по веб-каталагу с несколькими тысячами позиций.

Очень хочется добавить, что на мой взгляд иерархии в виду сложности представления в реляционном виде подходят только для определенного круга задачь. Решения данных задач зависят от средств реализации. Мне крайне сложно представить задачу в которой будет применяться ЧАСТО обновляемая иерархия, в таком случае ее просто не стоит использовать помоему. Возьмите к примеру веб-сайт, у которого несколько десятков тысяч уникальных хостов в сутки, и несколько сотен тысяч хитов, как вы себе представляете более частное использование вставки и обновления чем выборки в данном случае, эти величины просто несопоставимы, т.к. разница в несколько порядков минимум.

Насчет бейсика и пропусков. Можно придумать огромное кол-во фишечек, например ввести такое понятие как номер дерева, если у вас их несколько, ну и мало ли что еще. Но все это частные решения, которые мне в данном случае не интерестны, я хотел бы обсудить КОНКРЕТНОЕ решение САМОЙ идеи, без.

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

P.S> Я не хотел бы счас спорить о целесообразности или наоборот данного решения. Я хотел бы найти ошибки и исправить их, оставив качественно проработанный результат, для дальнейшего использования.
...
Рейтинг: 0 / 0
Готовое решение для работы с деревьями.
    #33238353
LeXa NalBat
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Paul Chabinsky, вы смотрели имеющиеся решения? http://sql.ru/forum/actualthread.aspx?tid=202006#1725807
...
Рейтинг: 0 / 0
Готовое решение для работы с деревьями.
    #33238413
4321
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Paul Chabinsky P.S> Я не хотел бы счас спорить о целесообразности или наоборот данного решения. Я хотел бы найти ошибки и исправить их, оставив качественно проработанный результат, для дальнейшего использования.
гм. Решение представления дерева в виде хранения номеров правых и левых узлов не ваше. Т.о. обсуждать его тут незачем. (Если не обсуждать его эффективность с т.з конкретных применений, чего вы не хотите). Вы хотите чтобы тут вылавливали ваших блошек? Кому это надо?

Я себе реализовал другое решение (из за того, что моя задача - это интенсивно растущее _преимущественно_ вверх (т.е. с _редкой_ пересадкой ветвей) дерево. При этом Ваше "право/левое" решение в среднем будет двигать полдерева (обновлять номера соседних узлов) - что затратно.

Очевидно, что хранение только родительского узла наиболее дешево по вставке, но видимо далеко не дешево по поиску иерархического подчинения (через вызов рекурсивной ф-ии). Но, насколько я помню, и "ваше" решение сводит к _агрегатам_ относительно частые задачи поиска (т.е. медленно, или я не прав?). Могу обсуждать преимущества моего решения против вашего, но ни ловить ваши ошибки реализации, ни предлагать для ловли блошек (того же рода) свои не собираюсь.


ЗЫ. Кстати сказать, к постгресу есть и _патчик_, который позволяет создавать рекурсивные запросы к деревьям. Где-то тут пробегали люди, его применявшие.
...
Рейтинг: 0 / 0
Готовое решение для работы с деревьями.
    #33238468
glebofff
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
...
Рейтинг: 0 / 0
Готовое решение для работы с деревьями.
    #33238625
Paul Chabinsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
2 LeXa NalBat
Видел ссылку что вы предложили. Но ничего конкретного для себя в ней не нашел к сожалению.

2 4321
1) Я нивкоем случае не претендую на то что я придумал как хранить дерево. И я да же не претендую на то что придумал как им управлять, я просто сделал аккуратную и лаконичную на мой взгляд реализацию. Которую хотел обсудить.
2) Joe Celko (его я считаю автором данного алгоритма) написал книжку про деревья в базе, я ее к сожалению не видил. Но в интернете именно от его имени не встречается ниодной реализации управления таким деревом, единственное что я нашел это процедура которая из id и parent_id, расчитывает поля left и right всего дерева.
3) Про агрегаты не понял к сожалению. Но насчет поиска могу только привести пример выборки дочерних узлов ноды:
Код: plaintext
SELECT * FROM Tree WHERE left > parent_left AND right < parent_right AND level > parent_level
Ну сравните с рекурсией сами...
4) Насчет коннект бай, это та же самая рекурсия... просто враппер для того чтоб вам не писать рекурсивную функцию самому вручную.

2 glebofff
Спасибо за ссылку, ее я то же видил, до того как сделал выложенный тут вариант. Мне просто данный вариант кажется несколько несистематезированным.
...
Рейтинг: 0 / 0
Готовое решение для работы с деревьями.
    #33238690
4321
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Paul Chabinskyпривести пример выборки дочерних узлов ноды:
Код: plaintext
SELECT * FROM Tree WHERE left > parent_left AND right < parent_right AND level > parent_level

кааца я вспомнил (поправьте если совру):
- тут у вас AND level > parent_level - лишнее (для правильного обхода по дереву левел есть ф-я лефт и райт)
и кажется само хранение левела - избыточно, а левел как раз и считается при помощи каунтов по неким запросам для "минимально избыточного" древа, хранящего только лефт и райт.


_
зы: я не работал с рекурсией. А именно со вспомогательной индексированной таблицей "полной структуры" дерева (все ссылки с узла на всех его предков по иерархии, без указания левела). Оценочно (число записей в такой структуре) <= (число записей в дереве)*(глубина дерева). Структура может строиться по иерархическому дереву в любой момент, но у меня строится триггерами синхронно. Количество вставляемых/удаляемых в структуру записей при вставке/удалении узла в конец/из конца ветки <=(глубина дерева) - как правило это много меньше половины узлов дерева - как в вашем случае; при перемещении/удалении целых ветвей естественно затрагивается гораздо больший пласт записей, ну дык это и более редкая операция.
...
Рейтинг: 0 / 0
Готовое решение для работы с деревьями.
    #33238802
Paul Chabinsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
2 4321
Да действительно поля parent_id и level избыточны, но значительно упрощают работу. Если они не нужны можно легко удалить их обработку.

С вашим методом я знаком, пробовал его, очень хароший метод, но мне он не подходит.

Обсудить я хотел бы именно Nested Set, и конкретную реализацию (устал повторять).

P.S> Вообще прихожу к выводу что я зря все это затеял.
...
Рейтинг: 0 / 0
Готовое решение для работы с деревьями.
    #33238841
4321
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Paul Chabinsky2 4321
Да действительно поля parent_id и level избыточны, но значительно упрощают работу. Если они не нужны можно легко удалить их обработку.

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

Paul ChabinskyОбсудить я хотел бы именно Nested Set, и конкретную реализацию (устал повторять).
P.S> Вообще прихожу к выводу что я зря все это затеял. ничего странного. Если найдется еще реализатор/пользователь _именно_ Nested Set, он видимо и поковыряется именно в _реализации_. Не расстраивайтесь. Я долго зарился на NS, но число узлов >> 10 000, знаете ли. Стоимость апдейта в среднем >>5000 записей _самого_ дерева на каждую операцию вставки не вдохновляла.
...
Рейтинг: 0 / 0
Готовое решение для работы с деревьями.
    #33239621
nostromo
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
To 4321:

4321зы: я не работал с рекурсией. А именно со вспомогательной индексированной таблицей "полной структуры" дерева (все ссылки с узла на всех его предков по иерархии, без указания левела). Оценочно (число записей в такой структуре) <= (число записей в дереве)*(глубина дерева). Структура может строиться по иерархическому дереву в любой момент, но у меня строится триггерами синхронно.

Если я правильно понял, то "индексированной таблицей "полной структуры"" -- это таблица, каждая запись которого соответствует ребру графа, если рассматривать дерево как граф. Если корень только 1, то количество ребер в точности равно N-1, где N -- количество количество узлов дерева (есть такая теорема). Если граф состоит из M деревьев и имеет N вершин, то соответственно в нем будет ровно N-M ребер.

По поводу хранения идеи реализации произвольного графа. Все задачи, связанные с графами можно разделить на два класса: 1) задачи, в которых узлы представляют достаточно сложные прикладные объекты. В этом случае граф имеет вспомогательную роль и его можно рассматривать как один из индексов для поиска; 2) задачи, в которых важна именно структура графа. В первую очередь, к ним относятся классические сетевые задачи из методов оптимизации (алгоритм Форда-Фолкерсона, алгоритм Краскала, поиск подграфов специального вида и т.д.).
Насколько я знаю, для задач второго сорта наиболее эффективными структурами представления графа являются матрица смежности и матрица инцедентности, т.е. БД имеет смысл использовать только для больших графов, да и то для хранения именно таких структур. В задачах первого сорта, по моим наблюдениям, графы сложнее деревьев встречаются редко (могу только векторные редакторы вспомнить). Впрочем, это лишь некоторое мое замечание, ничего против идеи я не имею.

To Paul Chabinsky:
Paul ChabinskyВозьмите к примеру веб-сайт, у которого несколько десятков тысяч уникальных хостов в сутки, и несколько сотен тысяч хитов, как вы себе представляете более частное использование вставки и обновления чем выборки в данном случае, эти величины просто несопоставимы, т.к. разница в несколько порядков минимум.
Не может ли случиться, что наилучшим решением в этом случае будет являться использование встроенного btree индекса, который, как я понимаю, и есть дерево для поиска, реализованное на низком уровне?
...
Рейтинг: 0 / 0
Готовое решение для работы с деревьями.
    #33239698
4321
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
блин, авторизовался, писал подробный ответ, старался высунув язык, как идиот, а этот долбанный сайт сожрал всё, выдал отлуп (куку он поселя, видимо) и не поперхнулся ... сцука

короче вы ,nostromo, несолько неправы. Что такое вспомогательная структура - см по ссылке автора в посте '1825061'.
...
Рейтинг: 0 / 0
Готовое решение для работы с деревьями.
    #33239770
nostromo
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
To 4321:
Да, просмотрел я эту ссылку, теперь понятно. Впрочем, теперь можно сделать новые оценки (ну математик я по профессии :)). Если имеем сбалансированное дерево c L уровнями, у которого каждый узел имеет k предков, то количество узлов (записей в первой таблице) будет равно: (k^L-1)/(k-1), а количество записей во второй, вспомогательной таблице: ((L-1)*k^(L+1) - L*k^L + k) / (k-1)^2.

Сама идея с двумя таблицами мне понравилась, может пригодиться.
...
Рейтинг: 0 / 0
Готовое решение для работы с деревьями.
    #33240323
Paul Chabinsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Меня спрашивали насчет произоводительности.

Компутер: Ноутбук, 1.3Мц, 512мб.

Я сделал дерево на 6к элементов (это конечно оч мало, но меня устраивает).
Корневой элемент содержить 10-ть дочерних (нулевой уровень),
Элементы первого еще по 10-ть
Элементы второго еще по 10-ть
Эдлементы третьего по 5-ть.

Создание такова дерева заняло 110 секунд (скриптом на PHP).

Не спрашивайте как получилось 6к

Так вот вставка узла в середину (как дочерний для среднего элемента четвертого уровня) дерева : 200-220мс
Вставка узла в начало первого уровня 400-500 мс.
Перемещение среднего узла с его потомками в начало первого уровня (хотя я мож че попутал) : 10мс.

Меня такие результаты устраивают.
...
Рейтинг: 0 / 0
Готовое решение для работы с деревьями.
    #33240334
4321
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
nostromo(ну математик я по профессии :)).Внушает! nostromoЕсли имеем сбалансированное дерево c L уровнями, у которого каждый узел имеет k предков, то количество узлов (записей в первой таблице) будет равно: (k^L-1)/(k-1), а количество записей во второй, вспомогательной таблице: ((L-1)*k^(L+1) - L*k^L + k) / (k-1)^2.долго думал. но раз есть что-то про "балананец" подумал, что речь таки о потомках, а не о предках. (каждый узел имеет не более 1 прямого предка и не более L иерархических), причем даже для случая "потомков" надо подправить формулировку -"каждый узел, за исключением терминальных... " ( имеет ). (кстати, а к какому типу девиаций относится имение предков, а к какому - потомков?)

Но вообще-то говоря интересны именно деревья (и графы) количество потомков у узла которых заведомо не фиксировано, и более того, зависит от узла (некие классификаторы сущностей с перестраиваемой классификацией). Т.ч. в чести более мягкие ограничения и более мягкие оценки.
...
Рейтинг: 0 / 0
Готовое решение для работы с деревьями.
    #33241441
nostromo
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Да, конечно, в моем последнем посте есть ошибки, должно быть так:

Если имеем сбалансированное дерево c L уровнями (полностью заполненными) , у которого каждый узел, кроме терминальных (не имеющих потомков) имеет k потомков , то количество узлов (записей в первой таблице) будет равно: (k^L-1)/(k-1), а количество записей во второй, вспомогательной таблице: ((L-1)*k^(L+1) - L*k^L + k) / (k-1)^2.

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

Далее, предположим, что L фиксировано, а k равстет, тогда количество вспомогательных записей эквивалентно L*k^(L-1). Если рассматривать рост количества записей доп. таблицы (обозначим через M количество записей доп. таблицы) относительно количества узлов дерева (обозначим через N), то получим оценку: M/N эквивалентно L-1 (при больших k). Т.о., для дерева с четырьмя уровнями иерархии, L=4 (на первом уровне находится только корневой узел), у которого каждый узел (кроме терминальных) имеет в среднем 10 потомков, можно ожидать, что количество строк доп. таблицы будет в 3 раза превышать количество узлов (в этом случае количество узлов N = 1111 в среднем).

В случае, когда k фиксировано, а L растет имеем: M/N эквивалентно (L-1)*k/(k-1).

Замечу, что эти оценки асимптотические (т.е. понятие больших значений не конкретизируется), и справедливы в среднем для всех деревьев с указанными ограничениями.

Аналогично можно рассмотреть случай, когда среднее количество потомков узла зависит от увовня (и эта зависимость известна), а также случай, когда имеется не одно дерево, а лес.
...
Рейтинг: 0 / 0
Готовое решение для работы с деревьями.
    #33241444
nostromo
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Предлагаю сравнить подходы с Nested Sets (NS) и доп. таблицей (ДТ) by 4321.

То, что приведено ниже, только мое мнение, на котором я сильно не настаиваю!

Типичные задачи:
1. получить всех предков определенного элемента.
NS - скорость пропорциональна глубине (необходим рекурсивный поиск,
поле level помогает слабо)
ДТ - быстрый запрос по доп. таблице

2. получить всех потомков определенного элемента.
NS - быстрый запрос
ДТ - быстрый запрос по доп. таблице

3. получить количество потомков данного элемента
NS - мгновенно
ДТ - быстрый запрос по доп. таблице

3. получить непосредственных потомков данного элемента
одинаково быстрый запрос для обоих подходов

4. получить информацию об уровне заданного элемента
NS - мгновенно
ДТ - быстрый запрос по доп. таблице (считаем предков)

5. проверка терминальности элемента
одинаково быстрый запрос для обоих подходов

6. поиск всех элементов заданного уровня
NS - быстрый запрос
ДТ - довольно тяжелый запрос (рекурсивный поиск от корня)

Особенности NS:
- сравнимельно маленькая избыточность данных
- поддержка и управление линейной упорядоченности непосредственных потомков
- довольно тяжелые обновления при модификации дерева.

Особенности ДТ:
- легкая модификация дерева
- приличная избыточность данных (вся доп. таблица)
- при слишком больших размерах доп. таблицы, количество записей в которой больше числа узлов, "быстрые" запросы могут оказаться не такими уж и быстрыми
...
Рейтинг: 0 / 0
Готовое решение для работы с деревьями.
    #33241582
4321
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
анализ (в части "рекурсий") не совсем верен:
к 1. если я правильно помню, то никаких рекурсий в НС не нужно, хвататет групповых запросов с агрегированием. Сейчас вспоминать не хочу, что именно нужно для 1. но можно поискать ссылки по н.с.
то же самое к
6. t_parents - доп таблица
автор
Код: plaintext
1.
2.
3.
SELECT l.id, ((count(l.pid ) -  1  ))::smallint AS "level" 
   FROM t_parents l
  GROUP BY l.id
HAVING ((count(l.pid ) -  1  ))::smallint =  5 ;
Суммарное время выполнения запроса:500 ms.
Время получения данных:109 ms.
получено строк: 2996
на большем объеме данных
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
-- Выполнение запроса:
SELECT l.id, ((count(l.pid ) -  1  ))::smallint AS "level" 
   FROM t_parents l
  GROUP BY l.id
HAVING ((count(l.pid ) -  1  ))::smallint =  5 ;

--Суммарное время выполнения запроса:3734 ms.
--Время получения данных:16 ms.
--Всего строк: 21311.
--строк не извлечено: 21211.
Select count(*) From tree;
 44565 
При этом никто не мешает мне хранить левел явным образом в собственно таблице дерева.
...
Рейтинг: 0 / 0
25 сообщений из 41, страница 1 из 2
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Готовое решение для работы с деревьями.
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]