| | | | |
Полный текст материала
Другие публикации автора: George Judkin
Цитата или краткий комментарий: «... В статье рассматривается несколько различных вариантов представления
деревьев в базах данных, а также реализация через SQL-запросы основных
операций по работе с этими деревьями ...» |
Важно:- Страница предназначена для обсуждения материала, его содержания, полезности, соответствия действительности и так далее. Смысл не в разборке, а в приближении к истине :о) и пользе для всех.
- Любые другие сообщения или вопросы, а так же личные эмоции в адрес авторов и полемика, не относящаяся к теме обсуждаемого материала, будут удаляться без предупреждения авторов, дабы не мешать жителям нормально общаться.
- При голосовании учитывайте уровень, на который расчитан материал. "Интересность и полезность" имеет смысл оценивать относительно того, кому именно предназначался материал.
- Размер одного сообщений не должен превышать 5К. Если Вам нужно сказать больше, сделайте это за два раза. Или, что в данной ситуации правильнее, напишите свою статью.
Всегда легче осудить сделанное, нежели сделать самому. Поэтому, пожалуйста, соблюдайте правила Королевства и уважайте друг друга.
Добавить свое мнение.
| | Содержит полезные и(или) интересные сведения | [1] | 6 | 66.7% | | | | Ничего особенно нового и интересного | [2] | 3 | 33.3% | | | | Написано неверно (обязательно укажите почему) | [3] | 0 | 0% | | Всего проголосовали: 9 | | | Все понятно, материал читается легко | [1] | 9 | 100% | | | | Есть неясности в изложении | [2] | 0 | 0% | | | | Непонятно написано, трудно читается | [3] | 0 | 0% | | Всего проголосовали: 9 |
[Древовидные структуры]
Отслеживать это обсуждение
Всего сообщений: 3127-01-2009 01:02сообщение от автора материала to Ивайло Гелов:
Значит, я правильно понял. И разница именно в том, что я у себя полностью перенес реализацию отношения на узлах в ассоциативную таблицу. Мне показалось, что раз уж она все равно имеется, то нет смысла дублировать данные задавая еще и на узле ссылку на непосредственного родителя. |
|
23-01-2009 17:01to Geo
Точнее, ето т.н. "Ассоциативная таблица". В оригинальной таблице, где хранятся данные - содержится простое дерево (с указателям к родительская запись). Ето основное представление, и оно может работать и без спомагательной ассоциативной таблице - она нужна только для ускорение поисках в глубине. Ускорение значительное - но и объем ассоциативной таблице тоже (к примеру, для 97000 узлов - получились 700000 ассоциациях). Материал для Farey fractions мне интересен, но до сих пор не сумел его реализировать - сложновато мне кажется, думаю что вариант с ассоциативной таблице достаточно прост для понимания и реализации, да и скорость тоже приличная.
Еще имеется похожая статия сюда [url]http://gsbelarus.com/gs/modules.php?name=News&file=article&sid=314[/url] |
|
30-12-2008 05:49сообщение от автора материала >>> у фиксированного есть один огромный плюс - его очень удобно создавать вручную
В общем то, любое из приведенных деревьев достаточно просто создается средствами самой СУБД (или я что-то неправильно понял). А основное преимущество фиксированного дерева состоит именно в том, что его можно вывести с любого узла обычным SQL-запросом. То есть не просто получить все узлы поддерева, начиная с определенного узла, но и получить их в нужном порядке, чтобы при последовательном выводе получалось дерево. В этом случае не нужды выкачивать все данные на клиента и обрабатывать там.
Мне в интернете попадались форумы с древовидными расположением сообщений, у которых было ограничение на максимальное количество уровней. Подозреваю, что был использован аналогичный механизм представления деревьев. |
|
30-12-2008 05:29На самом деле не всегда достаточно простого дерева, например у фиксированного есть один огромный плюс - его очень удобно создавать вручную, средствами самой СУБД или как в моем случае - в Excel таблице и импортом в MS Access. Такая технология осталась с начала работы над проектом, когда не было собственных средств редактирования деревьев. |
|
29-12-2008 06:44сообщение от автора материала Хорошо, будем считать, что меня уели: выкачивать данные в любом случае придется полностью. За исключением, разве что, случая, когда нужно вывести некое поддерево (нижнее замыкание узла). Однако прошу заметить, что вы сконцентрировались именно на простом дереве. А в этом случае я и сам, вроде, говорил, что нужно либо выкачивать целиком, либо подгружать по узлам.
Но если рассмотреть более общий случай, не зацикливаясь на одном единственном способе представления дерева, то тут уже возможны нюансы. Выгружать большое дерево на клиента в какие-то огромные структуры, а потом заниматься на клиенте подготовкой этих данных, чтобы вывести их в виде дерева... Есть в этом что-то не то. Почему бы не получить их сразу с сервера в упорядоченном виде, чтобы только вывести и все?
Собственно, именно вопросам организации хранения древовидных структур в базах данных из соображений удобной работы с ними (в том числе и вывода без предварительной обработки на клиенте) и посвящена данная статья. Но если вы желаете выгружать данные на клиента и возиться с ними самостоятельно, то, конечно же, вам достаточно простого дерева, и нет смысла огород городить с более сложными структурами. |
|
29-12-2008 05:36Согласен с Beg. Вариантов два:
1. полная загрузка дерева не требуется, загружаем узлы по мере обхода;
2. требуется полная загрузка дерева для дальнейшей работы с ним.
В обоих вариантах оптимально использовать "простое дерево". В первом - последовательно выполнять SQL-запрос, выдающий детей выбранного узла, во втором - один SQL-запрос, выдающий все дерево, а загрузку и упорядочивание производить средствами Delphi (тут уж поле для оптимизации куда больше, чем в SQL). |
|
26-12-2008 13:19Не могу согласится с автором статьи. Вы предлагаете выкачать все данные посредством рекурсивных запросов и построить дерево. Я предлагаю выкачать все данные одним запросом и уже рекурсивно обходить его, и в итоге построить дерево.
Вот до чего доводят избыточные ресурсы компьютера и хорошие каналы связи ;-)
А это даже немного задело за живое. Я в своем проекте использую деревья хранимые в БД. Сама БД - файл MS Access, лежащий на машине в локальной сети. Так вот, опыты показали, что рекурсивные запросы кушают ресурсов гораздо больше, чем один запрос + рекурсия по результатам. |
|
22-12-2008 01:34сообщение от автора материала to Beg:
Вот до чего доводят избыточные ресурсы компьютера и хорошие каналы связи ;-)
А зачем Вам тогда вообще базы данных нужны? Перекачайте сразу базу на совй компьютер и работайте с най локально. А я вот как-то не вижу смысла выкачивать даже несколько десятков мегабайт, если мне из этих десятков нужно всего десяток килобайт.
В общем, я сейчас всем говорю, что если хотите учиться правильно работать с базами данных, то прикидывайте решение для WEB (там сейчас все же передача данных медленная и дорогая). Можете представить себе сайт, в котором какая-то большая древовидная структура (какой-нибудь справочник размером на пару десятков мегабайт) будет выкачиваться сразу? Да если кто-нибудь что-нибудь подобюное у себя на страничке соорудит, его убьют сразу ;-)
* * *
to Ивайло Гелов:
Я Ваш ответ увидел. Насколько я успел понять (без знания PostgreSQL), в Вашем варианте рассматривается вариант того, что я в статье назвал "неограниченное дерево", но при этом кадый узел имеет ссылку на непосредственного предка, как в простом дереве. Сходу не пойму, даст ли это какие-то дополнительные преимущества.
Ссылки Ваши тоже просмотрел. Но тоже бегло. Заинтересовался идеей сложной индексации для задания порядка, но никак не смог найти время, чтобы детально изучить тот материал. |
|
21-12-2008 14:16Не могу не обратить внимание на то, что для простого дерева автор использует рекурсивные запросы к БД (назовем это так), тогда как гораздо быстрее было-бы одним запросом взять сразу всю таблицу и потом результат обойти рекурсивно. В своей программе делаю именно так. |
|
13-10-2008 13:26
CREATE TABLE "proba"."helper" (
"child_id" INTEGER NOT NULL,
"ancestor_id" INTEGER NOT NULL,
"depth" INTEGER NOT NULL,
CONSTRAINT "helper_idx" UNIQUE("ancestor_id", "child_id"),
CONSTRAINT "helper_pkey" PRIMARY KEY("child_id", "ancestor_id"),
CONSTRAINT "helper_chk" CHECK ((child_id <> ancestor_id) AND (depth > 0)),
CONSTRAINT "ancestor_fk" FOREIGN KEY ("ancestor_id")
REFERENCES "proba"."entity"("id")
ON DELETE CASCADE
ON UPDATE CASCADE
NOT DEFERRABLE,
CONSTRAINT "child_fk" FOREIGN KEY ("child_id")
REFERENCES "proba"."entity"("id")
ON DELETE CASCADE
ON UPDATE CASCADE
NOT DEFERRABLE
) WITHOUT OIDS;
CREATE INDEX "ancestor_idx" ON "proba"."helper"
USING btree ("ancestor_id");
CREATE INDEX "child_idx" ON "proba"."helper"
USING btree ("child_id");
Код работает в PostgreSQL - у меня нет возможности тестировать под другие ДБ
Если кому-то интересно, можно почитать сюда:
1. [URL]http://www.dbazine.com/oracle/or-articles/tropashko5[/URL]
2. [URL]http://arxiv.org/html/cs.DB/0401014[/URL]
3. [URL]http://arxiv.org/abs/cs.DB/0402051[/URL] |
|
13-10-2008 13:25
CREATE OR REPLACE FUNCTION "proba"."entity_ins" () RETURNS trigger AS
$body$
BEGIN
IF new.parent_id IS NOT NULL THEN
INSERT INTO proba.helper(child_id,ancestor_id,depth)
SELECT new.id,new.parent_id,1 UNION ALL
SELECT new.id,ancestor_id,depth+1 FROM proba.helper WHERE child_id=new.parent_id;
END IF;
RETURN NULL;
END;
$body$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 100;
CREATE TABLE "proba"."entity" (
"id" INTEGER NOT NULL,
"title" TEXT NOT NULL,
"parent_id" INTEGER,
CONSTRAINT "entity_pkey" PRIMARY KEY("id"),
CONSTRAINT "entity_chk" CHECK (parent_id <> id),
CONSTRAINT "parent_fk" FOREIGN KEY ("parent_id")
REFERENCES "proba"."entity"("id")
ON DELETE CASCADE
ON UPDATE CASCADE
NOT DEFERRABLE
) WITHOUT OIDS;
CREATE INDEX "parent_idx" ON "proba"."entity"
USING btree ("parent_id");
CREATE TRIGGER "entity_tr_del" AFTER UPDATE
ON "proba"."entity" FOR EACH ROW
EXECUTE PROCEDURE "proba"."entity_del"();
CREATE TRIGGER "entity_tr_ins" AFTER INSERT
ON "proba"."entity" FOR EACH ROW
EXECUTE PROCEDURE "proba"."entity_ins"();
|
|
13-10-2008 13:25Вот реализация деревьях через связывающую таблицу. Добавление, извлечение, уничтожение работают достаточно быстро (быстрее, чем Nested Sets аля Joe Celko), только обновление работает медленно - его скорость зависит в основном от высоту обрабатываемых под-деревьях (получается т.н. Cartesian product).
CREATE OR REPLACE FUNCTION "proba"."entity_del" () RETURNS trigger AS
$body$
BEGIN
-- first remove edges between all old parents of node and its descendants
DELETE FROM proba.helper WHERE (child_id=old.id OR child_id IN
(SELECT child_id FROM proba.helper WHERE ancestor_id = old.id)
) AND ancestor_id IN
(SELECT ancestor_id FROM proba.helper WHERE child_id = old.id);
-- then add edges for all new parents ...
IF new.parent_id IS NOT NULL THEN
INSERT INTO proba.helper(child_id,ancestor_id,depth)
SELECT new.id,new.parent_id,1
UNION ALL
-- ... to node itself
SELECT new.id,ancestor_id,depth+1 FROM proba.helper WHERE child_id=new.parent_id
UNION ALL
-- ... and its descendants
(
SELECT child_id,new.parent_id,depth+1 FROM proba.helper WHERE ancestor_id=new.id
UNION ALL
SELECT child_id,ancestor_id,depth FROM
(SELECT child_id FROM proba.helper WHERE ancestor_id=new.id) AS child
CROSS JOIN
(SELECT ancestor_id,depth+2 AS depth FROM proba.helper WHERE child_id=new.parent_id) AS parent
);
END IF;
RETURN NULL;
END;
$body$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 100;
|
|
15-08-2006 16:48сообщение от автора материала to jack128_:
Это все хорошо, если рассматриваемый узел -- лист. А если он является корнем некоего поддерева, то Вам придется обойти все это поддерево и всем узлам также изменить путь. |
|
13-08-2006 05:09if Node.NewParent <> Node.OldParent then
if Node.NewParent = nil then
Node.Path := ''
else
Node.Path := Node.NewParent.Path + '\' + IntToStr(Node.NewParent.ID); |
|
13-08-2006 05:082 Елена Филиппова
Но такое дерево сложно изменять и сложно обходить.
А в чем сложность то?
в псевдо коде, близком к дельфи:
if Node.NewParent <> Node.OldParent then
if Node.NewParent then
Node.Path := ''
else
Node.Path := Node.NewParent.Path + '\' + IntToStr(Node.NewParent.ID);
|
|
31-05-2006 10:18сообщение от автора материала to Andrew:
Вы бы уж писали явным образом, к чему относится ваше замечание, а то я долго пытался понять, где же у меня в статье имеется механизм с разделителями ;-)
Нехорошо цитировать себя самого, но:
05-05-2006 03:51
Хошь верь, хошь нет, но у меня в черновиках осталось предложение использовать для фиксированного дерева вместо группы полей Parent1,...,ParentN одно текстовое поле из расчета по 10 символов на уровень
То есть нормализация мной лично предполагалась при таком подходе изначально. Кстати, и выделение нужной подстроки в этом случае бужет выполняться быстрее. Но, в общем случае, требуется больше места. Соответственно, потребуется переходить к мемо-полям (или их аналогам). Отсюда большое замедление обработки. Как вариант -- годится, но не идеал. |
|
31-05-2006 06:39Тогда упираемся в количество записей в одно узле:-)) |
|
31-05-2006 06:34>>>Механизм с разделителями у вас получился несовершенным, так как при обходе дерева возникнут проблемы с сортировкой.
Это легко решается нормализацией индекса.
001.002
..
002.001
..
010.001
и т.д. |
|
31-05-2006 06:07Механизм с разделителями у вас получился несовершенным, так как при обходе дерева возникнут проблемы с сортировкой.
типа
1.2
...
10.1
2.1
Поэтому индексы надо вводить шестнадцатиречные - проблем с обходом такого дерева не будет!!! |
|
07-05-2006 15:26сообщение от автора материала >>> в каких реальных ситуациях может "возникнуть потребность взять потомков на два уровня в глубину"?
В прикладных задачах -- сколько угодно.
Первое, что приходит в голову, это что-то вроде: За данное преступление несет ответственность сам преступник и все его потомки мужского пола до седьмого колена включительно ;-)
Ну, а если взять что-то посерьезнее, то для больших структур может оказаться выгодным показ нескольких уровней иерархии как компромисс между потребностью в конкретизации и неспособностью воспринимать большие объемы информации, когда одного уровня иерархии для принятия решения недостаточно (не хватает конкретики), а все дерево целиком не воспринимается из-за больших объемов.
Например, предприятие с иерархической структурой подчинения: генеральный директор видит состояние уровня своих заметсителей и уровня руководителей департаментов, руководитель департамента -- начальников отделов и начальников групп, а уж начальники отделов -- все вплоть до рядовых исполниетелей. |
|
07-05-2006 04:54Спасибо за столь полный ответ, единственное, что не совсем понятно, в каких реальных ситуациях может "возникнуть потребность взять потомков на два уровня в глубину"? |
|
06-05-2006 15:36сообщение от автора материала >>> Мне почему-то кажется, что от поля Distance можно избавиться, просто взяв основную таблицу из первого варианта (с полем ParentID)
Абсолютно верно. Когда я понял, что не смогу нормально вывести неограниченное дерево, то сначала просто скопировал соответствующую функцию из простого дерева, со словами, что "раз уж не получилось, то оставим без изменений". Но потом все же переиграл этот момент.
>>> Или это ухудшит работу других операций, не упомянутых в статье?
Да, это одна из причин. Может, например, возникнуть потребность взять потомков на два уровня в глубину. Именно поэтому я сначала ввел для неограниченного дерева поле с номером уровня (как и для фиксированного дерева), но потом "спер" у Елены Филипповой идею с полем Distance. Такой вариант лучше, так как сами узлы (MainTable) ничего не знают о своем взаимном расположении, а все связи реализуются исключительно в таблице LinkTable.
Есть и еще одна причина. Желание избежать дублирования данных (так как при проектировании БД дублирование информации является ошибкой). Собственно говоря, введение для фиксированного дерева поля Lev и дублирование значение ID в одной из ссылок на родителя -- это тоже не лучший вариант. И введено это было исключительно для упрощения запросов (чтобы можно было реализовать на Local SQL). На MySQL я, скорее всего, от этих полей убежал бы.
И еще есть одна причина. Кроме показа возможной реализации я старался незаметно провести читателя по цепочке рассуждений. Сначала (простое дерево) у нас находятся в отношении только непосредственно связанные узлы. Отношение one-to-many реализуется полем-ссылкой. Потом отношение поменялось. Мы ставим в отношение каждому узлу всех его предков. Получаем отношение many-to-many, но за счет фиксированного максимального количества элементов отношения можем ограничиться множеством полей. И только в неограниченном дереве имеем в чистом виде many-to-many, что приводит к переносу реализации отношения в дополнительную таблицу. Именно чтобы подчеркнуть этот момент, все, касающееся отношения, было убрано из главной таблицы.
Пока человек учится, он должен строго руководствоваться правилами. А вот когда он предмет освоит, то имеет полное право эти правила нарушать. Потому что профи (в отличие от начинающего) осознает нарушение правила, знает, к каким последствиям это может привести, и не забудет принять меры, чтобы избежать вредного последствия нарушения правил. Так как в основном эта статья писалась как учебная (собственно, это один большой ответ на регулярно возникающие на КС вопросы по деревьям и БД), то и правила я старался строго соблюдать. |
|
06-05-2006 14:30Мне почему-то кажется, что от поля Distance можно избавиться, просто взяв основную таблицу из первого варианта (с полем ParentID). Тогда и обход будет полегче (без всяких JOIN-ов) и остальные функции вроде бы не изменятся.
Или это ухудшит работу других операций, не упомянутых в статье? |
|
05-05-2006 06:37Мне тоже приходилось иметь дело с деревьями и лесами :) Лес - это один из случиев графа. Я использовал простое дерево с дополнительным идентификатором дерева. Поскольку у меня размер одного дерева невелик, то главное было получить его одним запросом. А далее все обрабатывалось в памяти древовидным компонентом(TVirtualStringTree/TDBGridEh). С лесом вышло несколько сложнее, поскольку это был набор правил. Обрабатывал я его с помощю контейнерной библиотеки DeCAL. Оказалось деревья очень удобно обрабатывать с помощью контейнеров. Но это достаточно частное решение, вот и нестал делать статью на эту тему. |
|
05-05-2006 05:31сообщение от автора материала >>> Большие деревья нужно выводить по узлам
Константин!
Я с Вами в этом вопросе согласен. Действительно, в 99% реальных задач выввод большого дерева целиком не нужен. Более того, вреден. Работать с таким деревом нужно именно по узлам. Однако остается тот самый 1% экзотических случаев.
Кроме того, статья преследует еще одну цель (которая, правда, не сформулирована явно), а именно, учебную. Как можно проектировать БД и запросы, как при этом рассуждать, на что обращать внимание. Именно для программистов, которые уже освоили азы работы с базами данных и теперь хотят развиваться дальше, я и писал. |
|
05-05-2006 04:53Много раз делал деревья в БД, большие (10млн) и маленькие (меню - несколько узлов), всегда используя иерархическое дерево (простое, в Вашей статье). В других вариантах: диапазонное (фиксированное, в Ваше статье), фрактальное - не требовалось. По простой причине: маленькие деревья выводятся полностью на раз, например:
--
DECLARE SubItem CURSOR FOR SELECT ID, Item FROM #MenuLoad WHERE ID > 0 AND Modul = 0
DECLARE @HID INT, @HTM VARCHAR(100)
OPEN SubItem
FETCH NEXT FROM SubItem INTO @HID, @HTM
WHILE @@FETCH_STATUS = 0 BEGIN
EXEC UserMenuLoadNode @User, @HID, @HTM
FETCH NEXT FROM SubItem INTO @HID, @HTM
END
CLOSE SubItem
DEALLOCATE SubItem
--
SELECT * FROM #MenuLoad ORDER BY 1
--
…
CREATE PROCEDURE UserMenuLoadNode @User INT, @HID INT, @HTM VARCHAR(100)
AS
IF ( SELECT COUNT(*) FROM UsersMenu WHERE MenuUser = @User AND MenuHead = @HID ) > 0
INSERT INTO #MenuLoad SELECT @HTM + '_' + STR(MenuOrder,3),
MenuCaption, ISNULL(MenuAction,0),
IdMenuItem, @HTM, MenuOrder, Note
FROM UsersMenu
WHERE MenuUser = @User AND MenuHead = @HID
--
GO
здесь рекурсия заменяется использованием одной временной таблицы для обхода дерева, добавляя детей в её конец, мы дойдём до них и добавим уже внуков и так далее.
Большие деревья нужно выводить по узлам. Тут то же всё просто. |
|
05-05-2006 04:46Алексей, Юрий, вы меня опередили :о)
Я как раз хотела рассказать про такое дерево, где смешиваются признаки фиксированного и неограниченного по глубине. Я пришла к такому-же решению: вместо дополнительной таблицы используется поле PATH varchar, где хранится перечисление ID всех предков текущего узла.
Например "001/005/015" или в ином варианте.
Такая запись дерева позволяет элементарно одним запросом выводить всю иерархию любой ветки. Но такое дерево сложно изменять и сложно обходить.
Мне поневоле пришлось решать задачу, где одновременно требовалось быстро выводить всю иерархию любой ветки дерева и совершать его сложные обходы. Кроме того, дерево очень часто используется, но очень редко модифицируется.
Пришлось пойти на компромисс — функционально одно дерево физически реализовано двумя деревьями. Одно (в терминологии Geo) неограниченное (с доп. таблицей), второй фиксированное (с доп. полем PATH). Деревья между собой связаны. Неограниченное является первичным — оно модифицируется и используется в обходах. Фиксированное — вторично, оно заново генерится при изменении первичного дерева и используется только для показа иерархии.
Мой вариант может показаться странным и неоптимальным с точки зрения экономия дискового пространства и нормализации данных :о), но зато задача решается быстро. А это для меня намного важнее, так как у меня пространство не было ограниченным ресурсом, а вот быстродействие и нагрузка на сервер — существенное "тонкое" место.
|
|
05-05-2006 03:51сообщение от автора материала Алексей!
Хошь верь, хошь нет, но у меня в черновиках осталось предложение использовать для фиксированного дерева вместо группы полей Parent1,...,ParentN одно текстовое поле из расчета по 10 символов на уровень ;-) Хотел дописать в качесвте дополительной информации к части про фиксированное дерево. Не стал писать только потому, что статья сильно раздулась (особенно часть про фиксированное дерево).
Использование для задания ссылок одной строки упрощает написание запросов (за счет наличия в языках SQL функций работы со строками), однако замедляет скорость обработки (все же поиск подстроки в строке или вставка подстроки в строку -- это достаточно медленные операции). |
|
05-05-2006 01:46Полезная статья. Деревья издавна внушали человеку священный трепет своей рекурсивностью и фрактальностью, поэтому ясная и спокойная статья - то что надо.
Одно дополнение: когда-то приходилось встречать любопытный вариант реализации фиксированного дерева. Вместо группы полей Parent1, Parent2... ParentN используется одно длиннючее символьное поле. Иды родителей забиваются в эту строку, разделяясь либо по фиксированной длине, либо разделителями. Преимущество такого подхода в том, что, используя простые алгоритмы раболты с фиксированным деревом, мы можем наращивать его глубину без изменения структуры БД. Длину строки взять varchar с заведомым запасом - большинство серверов не хранят хвостовые пробелы, так что перерасхода дискового пространства не будет.
Недостаток - теряется декларативная ссылочная целостность на родителей. Впрочем, видел я это во времена FoxPro, так что такой проблемы тогда просто не стояло.
В связи с этим возникает мысль - а что если совместить использование структур неограниченного дерева и такой вот строки-родителей? Это решило бы проблему ссылочной целостности с одной стороны, и быстрого вывода дерева с другой, и все клеточки сравнительной таблицы стали бы "хорошо".
Разумеется, происходит некоторое дублирование данных, и некоторое замедление модификации. Но строку-родителей можно рассматривать как некий дополнительный индекс по отношению к данным дерева. И если работа с деревом происходит через специальный слой процедур с грамотным использованием транзакций (а так оно обычно и есть), то существование и обновление такого поля могло бы стать полностью прозрачным для программиста, использующего такую библиотеку. Для сомневающихся - написать процедуру "переиндексации".
|
|
04-05-2006 17:53сообщение от автора материала Спасибо за интересную ссылку. Приятно осознавать, что подобная проблема интересует не меня одного.
Теперь непосредственно по указанной Вами статье. Я ее пока только бегло просмотрел, но она уже вызывает у меня некоторые сомнения. А именно, речь идет о... как бы это сказать... вычислительной сложности выполнения операций на таком дереве. Например, для того чтобы добавить новый узел, взяв за родителя самый нижний узел в левой ветке (id=10 на картинке), потребуется модифицировать ВСЕ(!) узлы дерева. Извините, но я Вам безо всякой подготовки выдам более простую и понятную структуру, в которой можно добиться того же самого.
ID - первичный ключ
Name
Lev - уровень
Num - порядковый номер узла при выводе дерева
Подразумевается, что поле NUM задает "деревянный" порядок на множестве узлов.
1. Вывод дерева
SELECT * FROM Tree ORDER BY Num
2. Вывод поддерева, начиная с узла ($ID,$Lev,$Num)
Сначала определим значение поля Num следующего узла данного уровня ($LastNum)
SELECT MIN(Num) FROM Tree WHERE (Lev=$Lev) AND (Num>$Num)
Если запрос пуст, то в качестве $LastNum возьмем
SELECT MAX(Num)+1 FROM Tree
Теперь собственно вывод
SELECT * FROM Tree WHERE (Num>=$Num) AND (Num<$LastNum) ORDER BY Num
3. Добавление нового потомка к узлу ($ID,$Lev,$Num)
UPDATE Tree SET Num=Num+1 WHERE Num>$Num
INSERT INTO Tree (Lev,Num) VALUES ($Lev+1,$Num+1)
4. ... Ну, и так далее.
И не стоит огород городить с левыми правыми ключами.
Однако обе структуры (и та, которая приведена в Вашей статье, и та, которую сейчас построил я) будут нормально работать (на модификацию) на сравнительно небольших (по меркам баз данных) объемах. А в этом случае не имеет смысла возиться с базами данных. Загрузите все данные в оперативку (дин. массивы или списки) и работайте на здоровье. Будет намного быстрее.
P.S. Книгу Joe Celko давно хочу почитать, но пока не могу выкроить для этого достаточно времени.
P.P.S. Если Вас не затруднит, то в следующий раз подписывайтесь, пожалуйста, под своими сообщениями. А то очень трудно вести беседу с анонимом. |
|
04-05-2006 16:18Возможно Вам это пригодится
http://www.getinfo.ru/article610.html
хранения и удобноый вывод полного дерева или его ветви
Еще очень рекомендую Joe Celko's "Trees And Hierarchies In Sql For Smarties "Сообщение не подписано |
|
|
|