Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Иерархические структуры [MSSQL]


Страницы:   1 2 3

Хаванагил ©   (2007-03-22 14:58:00) 

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

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

так вот...
есть ли способ чтобы без рекурсии обойтись?


azamatufa   (2007-04-11 11:56:00) [1]

Q - это ADOQuery. При помощи него делаю запросы (в частности, определяю есть ли дети)


Jan1   (2007-04-11 10:08:00) [2]


> 1. Close

нет это не убирай.


Jan1   (2007-04-11 10:07:00) [3]


> Мои мысли такие: собрать все необходимые для удаления ID
> и одним запросом их удалить.

лишнее. да и на MS SQL не сработает :)

>  Q.Close;
>  Q.SQL.Clear;
>  Q.SQL.Add('SELECT * FROM Dep WHERE ParentID = ' + IntToStr(ID));
>
>  Q.Open;
>  Result := Q.RecordCount > 0;

1. Close, Clear убрать.
2. Используй параметры.
3. Проверку на пустой датасет лучше делать через IsEmpty.

>    while not Q.Eof do begin
>      Make_Del_List(Q.FieldByName('ID').AsInteger);


1. Что такое Q?
2. Где next?


Сергей М.   (2007-04-11 08:33:00) [4]


> но тогда ссылка должна мочь быть null (для самого верхнего
> уровня, над которым уже ничего нет)


Кто сказал, что "должна" ?
Что мешает сделать эту ссылку, например, равной первичному ключу этой же записи ?


azamatufa   (2007-04-11 07:42:00) [5]

Люди добрые! Здравствуйте! Я к Вам за помощью. Тут было сказано, что надо полюбить рекурсию. Вот я хочу начать это дело.

В общем имеется традиционная база - структура отделов организации.

ID   Name           ParentID
1    Экономики    0
2    Финансов     0
3    подЭконом    1
4    подФин         2
.........

Хочу написать рекурсию, которая бы удаляла Node и всех его детей и внуков и правнуков ....

Мои мысли такие: собрать все необходимые для удаления ID и одним запросом их удалить.

Вот попытка сбора ID

// -----------------------------
procedure TFormTree.Make_Del_List(ID:integer);
begin
 if HasChild(ID) then begin
   while not Q.Eof do begin
     Make_Del_List(Q.FieldByName('ID').AsInteger);
   end;
 end;
 List.Add(IntToStr(ID));
end;

// -------- есть ли дети ----------------------
function TFormTree.HasChild(ID:integer):boolean;
begin
 Q.Close;
 Q.SQL.Clear;
 Q.SQL.Add('SELECT * FROM Dep WHERE ParentID = ' + IntToStr(ID));
 Q.Open;
 Result := Q.RecordCount > 0;
end;


но она не так работает.. порой пропускает детей... если у одного детя есть внук..... вот (((


ANB   (2007-03-26 13:15:00) [6]


> Bless ©   (23.03.07 18:11) [36]

Похоже были организованы деревья в первых версиях 1С (до 6-ки точно).
Ничего - работоспособный вариант, но проблемы с количеством уровней.
И ограничение на количество цифр в ID ветки дерева.
Тока Cnt там нафиг не нужно.
Я в клиппере отказался от этой идеи - обычные парентовые деревья намного гибче.


MsGuns   (2007-03-24 14:00:00) [7]

Да тут критиковать-то нечего..
Для "классической" древесины с макс.вложенностью 5-6 можно использовать, правда будут хитромудрые запросы, которые к тому же будут тормозить.

Для неограниченных деревьев и, тем более, графов, неприемлима


Bless   (2007-03-23 18:11:00) [8]

можно для хранения данных использовать строковое поле.
Например, пусть есть такое дерево
Организация
Отдел1
Отдел2
 Подотдел21
Отдел3

Тогда таблица будет иметь поля
create table departments(
name varchar(100), название отдела
hcode varchar(50), иерархический код этой записи
cnt int  количество непосредственных подузлов(т.е. дети детей не считаются)

Содержимое таблицы:
Организация '00', 3
Отдел1  '0000', 0
Отдел2  '0001', 1
Подотдел21 '000100', 0
Отдел3  '0002', 0

Поля hcode и cnt обслуживают иерархические связи.
hcode - иерархический код записи
Поле cnt - это количество уже существующих у узла потомков.
Нужно для расчета hcode у новых потомков.

На каждый уровень иерархии выделяем два символа (можно больше, дело хозяйское).
И символы в моем примере ограничены цифрами (что не обязательно),
т.е. максимальное количество детей (именно детей, т.е. НЕПОСРЕДСТВЕННЫХ потомков)
у каждого родителя не будет превышать 100 штук.
hcode нового узла формируется как: код родителя + "свой код".
"Свой код" формируется на основании поля cnt. Например, если у родительской записи
hcode = '1234', cnt = 15, то у нового потомка hcode='123416'.
Думаю, идея понятна.

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

Выбрать ВСЕХ потомков записи с hcode=@hcode:

SET @len=LEN(@hcode)
SELECT * FROM departments WHERE hcode LIKE @hcode+'%' AND LEN(hcode)>@len


Удаление всех потомков

BEGIN TRAN
DELETE FROM departments
WHERE hcode LIKE '@hcode+'%' AND LEN(hcode)>@len

SELECT @err = @@ERROR, @rc=@@ROWCOUNT
IF @err<>0 GOTO FAIL_EXIT

UPDATE departments SET cnt=cnt-@rc WHERE hcode=@hcode

SET @err = @@ERROR
IF @err<>0 GOTO FAIL_EXIT

COMMIT TRAN
RETURN 0

FAIL_EXIT:

ROLLBACK TRAN
RETURN @err


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


Jan   (2007-03-23 12:49:00) [9]


> Ega23 ©   (22.03.07 16:39) [22]

можно проще и быстрее.

declare @ParentID [uniqueidentifier]

set @ParentID = 'bla-bla-bla'

declare @Stack table ([id] [uniqueidentifier])
declare @Result table ([id] [uniqueidentifier])

insert into @Stack
select [ID] from [TreeTable]
where [ParentID] = @ParentID

while (exists(select * from @Stack))
begin
set @ParentID = (select top 1 [id] from @Stack)

insert into @Stack
select [ID] from [TreeTable]
where [ParentID] = @ParentID

insert into @Result
select @ParentID

delete from @Stack
where [id] = @ParentID
end

select * from @Result


Jan   (2007-03-23 11:40:00) [10]


> Ega23 ©   (22.03.07 16:39) [22]

идея классная, но зачем здесь:
@TreeLevel?
@Type?


Ega23   (2007-03-22 19:13:00) [11]


> Использование CTE в 2005 обходит это ограничение.


Мы только-только щупать ExpressEdition начали...


Empleado   (2007-03-22 18:51:00) [12]


>  MSSQL НЕ ПОДДЕРЖИВАЕТ стек вызовов более 32

Использование CTE в 2005 обходит это ограничение.


Ega23   (2007-03-22 17:30:00) [13]


> до боли мой код напоминает... ) ну sniknik, так sniknik


Да, блин, не помню я. Может и твой. Если твой - сейчас комент добавлю (последнее время уже взял за правило - если не мой код, то комент, чей)


clickmaker   (2007-03-22 17:21:00) [14]


> [29] Reindeer Moss Eater ©   (22.03.07 17:19)

чтобы понять рекурсию, надо сначала понять рекурсию...


Reindeer Moss Eater   (2007-03-22 17:19:00) [15]

Полюбить рекурсию и жить спокойно до старости.


clickmaker   (2007-03-22 17:13:00) [16]


> Идею, вроде, sniknik как-то подкинул

до боли мой код напоминает... ) ну sniknik, так sniknik


Хаванагил   (2007-03-22 17:03:00) [17]


> Ega23 ©   (22.03.07 16:44) [26]

да, 32 вполне хватит! у меня где-то 6-8 уровней в дереве етом выходит..

спасибо всем, очень интересно! )


Ega23   (2007-03-22 16:44:00) [18]

Да, вот ещё что. MSSQL НЕ ПОДДЕРЖИВАЕТ стек вызовов более 32.
Хотя я для своих задач так и не смог придумать такого дерева, где уровень вложенность был бы больше 32...


Хаванагил   (2007-03-22 16:41:00) [19]


> Ega23 ©   (22.03.07 16:39) [22]

ууу...
спасибо, сохраню пока - дома разберусь))


ANB   (2007-03-22 16:40:00) [20]


> Но рекурсией она быть не перестанет. Кто бы её не крутил.
> .. :)

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


Страницы:   1 2 3

Наверх

Титульная страница Поиск, карта сайта Написать письмо