Форум: "Базы";
Текущий архив: 2003.03.27;
Скачать: [xml.tar.bz2];
ВнизПостроение дерева Найти похожие ветки
← →
Илайдж (2003-03-03 17:22) [0]Есть табличка
ID PARENT_ID NAME
1 0 Основная ветка
2 1 Подветвь 1
3 2 Подветвь 1-1
4 1 Подветвь 2
5 4 Подветвь 2-1
Как:
1 - вытащить одним запросом всю структуру дерева
2 - построить его?
Заранее сенк.
← →
gsu (2003-03-03 17:30) [1]TTreeView, TTreeNodes
>> 1 - вытащить одним запросом всю структуру дерева
В каком виде ? как в файле ?
>> 2 - построить его?
Add, AddChild
← →
Илайдж (2003-03-03 17:35) [2]Мне нужно построить дерево вида
+ Основная ветка
- Подветвь 1
- Подветвь 1-1
- Подветвь 2
- Подветвь 2-1
Хотелось бы кусочек кода на моем примере
← →
calm (2003-03-03 17:44) [3]выбирай все с
order by Parent_id, id
Получается упорядоченная структура. С ней можно работать и построить дерево. Это если одним запросом.
Есть несколько компонент, которые строят дерево на основе такой структуры, но для построения дочерних узлов каждого узла вызывается отдельный запрос.
Я и сам разработал один небольшой компонент на эту тему. Но мне нужно было работать с небольшими таблицами, поэтому компонент наверняка не слишком эффективно будет работать с большими объемами данных.
← →
gsu (2003-03-03 17:47) [4]procedure TForm1.Button2Click(Sender: TObject);
var
tn: TTreeNode;
begin
with TreeView1.Items do
begin
Clear;
tn:=Add(nil, "1");
tn:=AddChild(tn, "11");
tn:=Add(tn, "12");
tn:=AddChild(tn, "121");
tn:=Add(tn, "122");
Add(tn, "123");
tn:=tn.Parent;
Add(tn, "13");
tn:=Add(nil, "2");
tn:=AddChild(tn, "21");
tn:=Add(tn, "22");
Add(tn, "23");
end;
end;
И, вообще, смотрите примеры
← →
Илайдж (2003-03-03 17:49) [5]А..я кажется понял, упорядочить таким образом, а потом прогнать через while not eof?
Сенк, попробую.
← →
gsu (2003-03-03 17:56) [6]так те с работой в БД, где то я видел статью по DBTreeView
А строить снимок дерева можно разными способами, самое простое - сохранить в файл ...
← →
Илайдж (2003-03-03 18:00) [7]Все, все получилось.
Спасибо всем большое.
← →
Serginio (2003-03-03 21:28) [8]Если локальная база то
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;
Извиняюсь за неточности но принцип такой
← →
Alander (2003-03-04 08:14) [9]Кстати в тему, может кто знает есть такой компонент 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). Подскажите пожалуйста как мне этим компонентом воспользоватья чтобы это дерево рекурсивно не долбать!!!
← →
Соловьев (2003-03-04 09:18) [10]http://www.delphikingdom.com/helloworld/dbtreeview.htm
а еще
http://www.ibase.ru
Лучше сделать ручками. Я сдела и не жалею, а то у чужих компонент всегда какие-то ограничения.
← →
_Shade (2003-03-04 11:01) [11]>Serginio
А спорим удастся! И никаких рекурсивных дёрганий БД не надо. А вот что, действительно, надо - так это подумать немного.
← →
Serginio (2003-03-04 11:56) [12]Спорить не буду но интересно помотреть например на 5 уровневую подчиненность.
← →
Sir Alex (2003-03-04 12:08) [13]ИМХО, самый правильный вариант работы с DBTree:
Загружаешь первый уровень:
SELECT * FROM TREE WHERE PARENTID IS NULL
Когда пользователь открывает одну из веток, подгружаешь второй уровень (только для выбранного первого уровня)
SELECT * FROM TREE WHERE PARENTID=1
и т.д.
P.S. Немного сложновато в реализации, но зато быстро работает на больших деревьях, и зря не тратит память.
← →
Serginio (2003-03-04 12:15) [14]Для (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;
← →
EthernalWonderer (2003-03-04 15:16) [15]Ваша процедура будет выполняться минуты 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 16:20) [16]2(EthernalWonderer) не совсем понятно. Я поня человеку нужно сделать как в 1С. В приведенном алгоритме подчиненность уровню а не конкретному ParentID. Можно сделать и на подчиненности ParentID но тогда надо строить индекс (так как ID может быть в большом диапозоне) вместо Array. Такая реализация тоже есть.
← →
Sir Alex (2003-03-05 10:50) [17]2 Serginio
А зачем пользователю раскрывать все дерево?
Как правило пользователь идет по одной ветке до самого низкого уровня...
← →
Serginio (2003-03-05 12:08) [18]2(Sir Alex ©) Полностью согласен. Я просто показал это еще один способ полного заполнения. Но еще надо учитывать возможные изменения, такие как перенос в другую группу и добавление записей а здесь могут быть грабли.
← →
Conder (2003-03-05 16:59) [19]Я заполнял дерево такой вот рекурсивной процедурой:
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
← →
Wellslava (2003-03-05 17:07) [20]а у меня вообще компонентик есть... :)))
← →
EthernalWonderer (2003-03-07 14:02) [21]1. Господа, переоткрывать запрос в каждом дочернем узле, как это предлагает Serginio, слишком медленно. Вариант Сэра Алекса хорош только в отдельных случаях. В целом ожидать, что юзеру понравится сидеть и ждать по одной - две секунды при открытии каждого узла смешно. Единственный нормальный метод - скачивать ВСЁ дерево одним запросом, и формировать TreeNodes из результата запроса на клиенте. Это - главное. Реализации могут быть разные.
2. В приведённом мной примере LEVEL - столбец, возвращаемый Oracle при использовании фразы connect By. Если используется другая база - запрос должен быть подправлен (В Interbase у меня была написана специальная SQL - процедура, работающая как connect by и возвращающая уровень подветви).
← →
Serginio (2003-03-07 20:21) [22]Для (EthernalWonderer). Я только за "скачивать ВСЁ дерево одним запросом, и формировать TreeNodes ", на клиенте очень просто сгрупптровать под то,что тебе нужно. Я противник SQL (реляционных баз), а сторонник ООБД (но не втом виде, что предлагают) хотя на них и не программировал суть понятна, а вытянуть иерархические данные из плоских баз хоть и не проблема, но не правильна с точки зрения ОО программирования. И меня убивает пристрастие большинства к SQL и нормализации огромного количества данных там где вообще индекс совсем не нужен. Технологии побеждают здравый смысл. Гуда мы катимся ТОВАРИЩИ. Это простой крик души. А вопрос в топике это небольшой пример с чем мы имеем дело и наш взгляд на ООП.
← →
dim- (2003-03-07 21:31) [23]Данная процедура возвращает дерево начиная с узла передаваемого в качестве параметра в процедуру
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
← →
Sir Alex (2003-03-10 11:33) [24]2 EthernalWonderer
Ну не знаю... когда у меня было несколько тысяч узлов в дереве, пользователям совсем не нравилось ждать конца загрузки всего дерева. Пришлось сделать по моему варианту. Кроме того, как Вы заметили, у Interbase нет функции "Connect by" (Хотя я и не знаком с Oracle, но догадываюсь для чего она нужна), следовательно, мне пришлось-бы написать свою функцию "Level" (я ее писал раньше).
> В целом ожидать, что юзеру понравится сидеть и ждать по
> одной - две секунды при открытии каждого узла смешно
Действительно смешно, что SQL сервер будет выполнять такой простецкий запрос (SELECT NAME, ID FROM TREE WHERE PID IS NULL) несколько секунд. Практика показывает, что пользователь даже не замечает, что дерево формируется "на лету".
P.S. Не стоит забывать, что SQL Сервер - это НЕ ТУПОЕ хранилеще информации, он предназначен для выборки небольшого кол-ва записей из огромной базы.
P.S. Ах да, согласен с Вами в том случае, если дерево небольшое (1-500 узлов)
← →
EthernalWonderer (2003-03-11 10:12) [25]> Sir Alex ©
Я не говорил, что Ваш вариант плох. Возможно, для решения Вашей задачи он даже очень хорош. Он даже оптимален с точки зрения объёма передаваемых по сети данных, что может быть причиной его выбора.
В случае же, если вычислительной мощности сервера маловато, а юзеров много, то моя практика :) показывает, что периодические "зависания" программы при раскрытии узлов всё же случались. При этом нарушалось одно из базовых правил: нельзя "замораживать" программу без уведомления пользователя.
Поэтому выбран другой вариант: всё дерево грузится в течении 2-5 секунд (в зависимости от текущей загрузки сервера), при этом высвечивается прогресс - индикатор, зато затем работа программы не зависит от загрузки сети и сервера.
Страницы: 1 вся ветка
Форум: "Базы";
Текущий архив: 2003.03.27;
Скачать: [xml.tar.bz2];
Память: 0.51 MB
Время: 0.008 c