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

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

Построение дерева [D6, Win2k]


Страницы:   1 2

Илайдж ©   (2003-03-03 17:22:00) 

Есть табличка
ID PARENT_ID NAME
1 0 Основная ветка
2 1 Подветвь 1
3 2 Подветвь 1-1
4 1 Подветвь 2
5 4 Подветвь 2-1

Как:
1 - вытащить одним запросом всю структуру дерева
2 - построить его?

Заранее сенк.


EthernalWonderer   (2003-03-11 10:12:00) [1]

> Sir Alex ©
Я не говорил, что Ваш вариант плох. Возможно, для решения Вашей задачи он даже очень хорош. Он даже оптимален с точки зрения объёма передаваемых по сети данных, что может быть причиной его выбора.
В случае же, если вычислительной мощности сервера маловато, а юзеров много, то моя практика :) показывает, что периодические "зависания" программы при раскрытии узлов всё же случались. При этом нарушалось одно из базовых правил: нельзя "замораживать" программу без уведомления пользователя.
Поэтому выбран другой вариант: всё дерево грузится в течении 2-5 секунд (в зависимости от текущей загрузки сервера), при этом высвечивается прогресс - индикатор, зато затем работа программы не зависит от загрузки сети и сервера.


Sir Alex   (2003-03-10 11:33:00) [2]

2 EthernalWonderer

Ну не знаю... когда у меня было несколько тысяч узлов в дереве, пользователям совсем не нравилось ждать конца загрузки всего дерева. Пришлось сделать по моему варианту. Кроме того, как Вы заметили, у Interbase нет функции "Connect by" (Хотя я и не знаком с Oracle, но догадываюсь для чего она нужна), следовательно, мне пришлось-бы написать свою функцию "Level" (я ее писал раньше).


> В целом ожидать, что юзеру понравится сидеть и ждать по
> одной - две секунды при открытии каждого узла смешно

Действительно смешно, что SQL сервер будет выполнять такой простецкий запрос (SELECT NAME, ID FROM TREE WHERE PID IS NULL) несколько секунд. Практика показывает, что пользователь даже не замечает, что дерево формируется "на лету".

P.S. Не стоит забывать, что SQL Сервер - это НЕ ТУПОЕ хранилеще информации, он предназначен для выборки небольшого кол-ва записей из огромной базы.

P.S. Ах да, согласен с Вами в том случае, если дерево небольшое (1-500 узлов)


dim-   (2003-03-07 21:31:00) [3]

Данная процедура возвращает дерево начиная с узла передаваемого в качестве параметра в процедуру

CREATE proc dbo.get_all_children @id int
as begin
set nocount on
set ansi_warnings off
declare @row int

create table #tmp_t (id int, name varchar(150), delet bit, sort varchar(100))

insert #tmp_t (id,name,delet,sort)
select id,name,delet,str(id) from podrazd where (id = @id) and (delet=0)
set @row=@@rowcount

while @row> 0
begin
insert #tmp_t (id,name,delet,sort)
select p.id, p.name, p.delet , str(p.id)+str(p.bos)
from podrazd p
inner join #tmp_t on (
p.bos=#tmp_t.id and p.delet<>1
and p.id not in (select id from #tmp_t))
set @row=@@rowcount
continue
end
select * from #tmp_t
order by sort
end



Serginio   (2003-03-07 20:21:00) [4]

Для (EthernalWonderer). Я только за "скачивать ВСЁ дерево одним запросом, и формировать TreeNodes ", на клиенте очень просто сгрупптровать под то,что тебе нужно. Я противник SQL (реляционных баз), а сторонник ООБД (но не втом виде, что предлагают) хотя на них и не программировал суть понятна, а вытянуть иерархические данные из плоских баз хоть и не проблема, но не правильна с точки зрения ОО программирования. И меня убивает пристрастие большинства к SQL и нормализации огромного количества данных там где вообще индекс совсем не нужен. Технологии побеждают здравый смысл. Гуда мы катимся ТОВАРИЩИ. Это простой крик души. А вопрос в топике это небольшой пример с чем мы имеем дело и наш взгляд на ООП.


EthernalWonderer   (2003-03-07 14:02:00) [5]

1. Господа, переоткрывать запрос в каждом дочернем узле, как это предлагает Serginio, слишком медленно. Вариант Сэра Алекса хорош только в отдельных случаях. В целом ожидать, что юзеру понравится сидеть и ждать по одной - две секунды при открытии каждого узла смешно. Единственный нормальный метод - скачивать ВСЁ дерево одним запросом, и формировать TreeNodes из результата запроса на клиенте. Это - главное. Реализации могут быть разные.

2. В приведённом мной примере LEVEL - столбец, возвращаемый Oracle при использовании фразы connect By. Если используется другая база - запрос должен быть подправлен (В Interbase у меня была написана специальная SQL - процедура, работающая как connect by и возвращающая уровень подветви).


Wellslava   (2003-03-05 17:07:00) [6]

а у меня вообще компонентик есть... :)))


Conder   (2003-03-05 16:59:00) [7]

Я заполнял дерево такой вот рекурсивной процедурой:
procedure TViewForm.BuildNode(Parent:TTreeNode; ParentID:Integer; Tree:TTreeView);
var
aQuery : TADOQuery;
aCurrNode : TTreeNode;
begin
With Tree_DM do
begin
aQuery := TADOQuery.Create( nil );
try
with aQuery do begin
Connection := Main_ADOConnection;
SQL.Text := Format("SELECT * FROM Tree WHERE Parent=%d",[Parentid]);
Active := true;
while not aQuery.EOF do begin
aCurrNode := Tree.Items.AddChildObject(Parent,FieldByName("Group_Name").AsString, pointer(FieldByName("index_").AsInteger));
BuildNode(aCurrNode,FieldByName("index_").AsInteger,Tree);
Next( );
end; // while
Active := False;
end; // with
finally
aQuery.Free( );
end; // try
end;
end; // BuildNode


Serginio   (2003-03-05 12:08:00) [8]

2(Sir Alex ©) Полностью согласен. Я просто показал это еще один способ полного заполнения. Но еще надо учитывать возможные изменения, такие как перенос в другую группу и добавление записей а здесь могут быть грабли.


Sir Alex   (2003-03-05 10:50:00) [9]

2 Serginio
А зачем пользователю раскрывать все дерево?
Как правило пользователь идет по одной ветке до самого низкого уровня...


Serginio   (2003-03-04 16:20:00) [10]

2(EthernalWonderer) не совсем понятно. Я поня человеку нужно сделать как в 1С. В приведенном алгоритме подчиненность уровню а не конкретному ParentID. Можно сделать и на подчиненности ParentID но тогда надо строить индекс (так как ID может быть в большом диапозоне) вместо Array. Такая реализация тоже есть.


EthernalWonderer   (2003-03-04 15:16:00) [11]

Ваша процедура будет выполняться минуты 2-5, в зависимости от
количества узлов. Так будет в 50 раз быстрей:

Procedure LoadTVCat(N: TTreeNodes);
Var LastNode: Array[0..12] Of TTreeNode;
begin
q.SQL = "SELECT ID,LEVEL-1,CAPTION FROM CAT";
q.Open;
With q Do While Not Eof Do Begin
LastNode[Fields[1].AsInteger+1] := N.AddChildObject(
LastNode[Fields[1].AsInteger], //L_PARENT
Fields[2].AsString, //CAPTION
Pointer(Fields[0].AsInteger)); //ID
Next;
End;
q.Close;
end;


Serginio   (2003-03-04 12:15:00) [12]

Для (Sir Alex ©) Если все ветки откроешь то в итоге все равно получишь мой вариант.
Там небольшая ошибочка
If NotAssigned(tn) Then
tn2:=TreeView1.Items.Add(nil, Qry.FieldByName("NAME").AsInteger)
Else
tn2:=TreeView1.Items.AddChild(tn, Qry.FieldByName("NAME").AsSting)

AddChild(tn2,TreeView1,Qry.FieldByName("ID").AsInteger));
end;


Sir Alex   (2003-03-04 12:08:00) [13]

ИМХО, самый правильный вариант работы с DBTree:

Загружаешь первый уровень:
SELECT * FROM TREE WHERE PARENTID IS NULL

Когда пользователь открывает одну из веток, подгружаешь второй уровень (только для выбранного первого уровня)

SELECT * FROM TREE WHERE PARENTID=1

и т.д.

P.S. Немного сложновато в реализации, но зато быстро работает на больших деревьях, и зря не тратит память.


Serginio   (2003-03-04 11:56:00) [14]

Спорить не буду но интересно помотреть например на 5 уровневую подчиненность.


_Shade   (2003-03-04 11:01:00) [15]

>Serginio
А спорим удастся! И никаких рекурсивных дёрганий БД не надо. А вот что, действительно, надо - так это подумать немного.


Соловьев   (2003-03-04 09:18:00) [16]

http://www.delphikingdom.com/helloworld/dbtreeview.htm
а еще
http://www.ibase.ru
Лучше сделать ручками. Я сдела и не жалею, а то у чужих компонент всегда какие-то ограничения.


Alander   (2003-03-04 08:14:00) [17]

Кстати в тему, может кто знает есть такой компонент abcDBTreeView, если кто пользовался, опишите пожалуйста как им пользоваться, а конкретнее, что значат свойства в DataSources. У меня такая структура: таблица Param: ID_Param, IParent(ид родителя), BGroup (1 or 0). VKind : ID_Kind. И ParamKind : ID_ParamKind, IValueKind(ссылается на ID_Kind из VKind), IParameter (ссылается на ID_Param из Param). Так вот все дерево образуют Param"ы а на последнем уровне находятся ID_ParamKind"ы, которые образуются пересечением ValueKind"ов и Param"ов (если у Param"а BGroup=0). Подскажите пожалуйста как мне этим компонентом воспользоватья чтобы это дерево рекурсивно не долбать!!!


Serginio   (2003-03-03 21:28:00) [18]

Если локальная база то
http://www.1c.hippo.ru/cgi-bin/predownl.cgi?id=2019
есть пример создания такого дерева.
А одним запросом выбрать не получится если количество уровней больше 2. Вернее нужно строить запросы по уровням рекурсивно тип
Function AddChild(tn: TTreeNode;
TreeView:TreeView; ParentID:Integer);
Var QRY:TQuery;
tn2: TTreeNode;
begin

QRY.Text="Select * From Where ParentID=:ParentID";
QRY.Open;
QRY.First
While Not Qry.Eof Do
Begin

If NotAssigned(tn) Then
tn2:=TreeView1.Items.Add(nil, Qry.FieldByName("ID").AsInteger)
Else
tn:=TreeView1.Items.AddChild(tn, "11")
AddChild(tn2,TreeView1,Qry.FieldByName("ParentID").AsInteger));
end;

end;
Извиняюсь за неточности но принцип такой


Илайдж   (2003-03-03 18:00:00) [19]

Все, все получилось.

Спасибо всем большое.


gsu   (2003-03-03 17:56:00) [20]

так те с работой в БД, где то я видел статью по DBTreeView
А строить снимок дерева можно разными способами, самое простое - сохранить в файл ...


Страницы:   1 2

Наверх

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