Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Базы данных и репортинг > Обзор таблицы через TreeView1


Автор: Ddddddelphi 9.12.2004, 23:35
Создал таблицу и псевдоним с именем Lavocka.
Таблица такая - таблица описывает предприятие, у каждого рабочего имеется свой универсальны код в таблице,
и дополнительный код, который означает - у кого в подчинение данный рабочий, вот таблица:
code name dopcode proffession
0033 bos 0000 director
0084 ivanov 0033 menejer
0085 vasichkin 0033 menejer
0097 ohlamonov 0084 menejer's p0moshnik
0097 ohlamonov 0085 menejer's p0moshnik

Мне нужно через дерево TreeView1, используя SQL -запросы отобразить базу данных,
следующим образом :

-bos
_ _ _-ivanov
_ _ _ _ _ _ohlamonov
_ _ _-vasichkin
_ _ _ _ _ _ohlamonov
подскажите пожалуйста, как нужно чего делать????????????? smile smile


Автор: SPrograMMer 10.12.2004, 13:23
УУУ, чего захотел!
компонент TDBTree поищи в каком-нить яндексе.

Автор: Dimich 10.12.2004, 13:38
http://forum.vingrad.ru/index.php?showtopic=36017

Автор: BSV_Sergey 10.12.2004, 14:41
Для построения данного дерева нужно использовать рекурсию. Вариантов может быть несколько. Если БД на SQL сервере то могу предложить следующее:
Создать на сервере 2 хранимые процедуры, которые используя рекурсию сформируют промежуточную таблицу, на основании которой delphi построит дерево.
Процедура 1
Код

CAREATE PROCEDURE TREEADD
AS
BEGIN

DECLARE @TREEID AS INT,
@TREEUPID AS INT,
@DESC AS VARCHAR(255),
@POZ AS INT

CREATE TABLE #RESULT(TYPE VARCHAR(10),
      DESCR VARCHAR(255),
      IDREC INT)

DECLARE #PRIMARY_TREE CURSOR LOCAL FAST_FORWARD FOR
SELECT
TREEID,
TREEUPID,
DESCRIPTION
FROM STRUCTURE
WHERE TREEUPID = 0

OPEN #PRIMARY_TREE
WHILE @@ERROR=0
  BEGIN
   FETCH NEXT FROM #PRIMARY_TREE INTO @TREEID, @TREEUPID, @DESC
    IF @@FETCH_STATUS<>0 BREAK
INSERT INTO #RESULT
VALUES('Nil',@DESC,@TREEID)
SET @POZ = (SELECT COUNT(*) FROM #RESULT)-1
EXECUTE RECURSADD @TREEID,@POZ
  END  
CLOSE #PRIMARY_TREE
DEALLOCATE #PRIMARY_TREE

SELECT * FROM #RESULT

DROP TABLE #RESULT
END


Процедура 2

Код

PROCEDURE RECURSADD
@TREEID INT,
@POZITION INT
AS

DECLARE #REVERS CURSOR LOCAL FAST_FORWARD FOR
SELECT TREEID, TREEUPID, DESCRIPTION
FROM  STRUCTURE
WHERE TREEUPID = @TREEID

OPEN #REVERS

DECLARE @TRID AS INT,
@TRUPID AS INT,
@DESCR AS VARCHAR(255),
@POZ AS INT

WHILE @@ERROR=0
  BEGIN
FETCH NEXT FROM #REVERS INTO @TRID, @TRUPID, @DESCR
       IF @@FETCH_STATUS <>0 BREAK
INSERT INTO #RESULT VALUES(CAST(@POZITION AS VARCHAR),@DESCR,@TRID)
SET @POZ = (SELECT COUNT(*) FROM #RESULT)-1
EXECUTE RECURSADD @TRID,@POZ
  END  

CLOSE #REVERS
DEALLOCATE #REVERS



В Delphi пишем процудеру формирования дерева:

Код

with ADOQuery2 do
 begin
  SQL.Clear;
  SQL.Add('exec TREEADD');
  Open;
 end;
ADOQuery2.First;
TreeView1.Items.Clear;
TreeView1.Items.BeginUpdate;
while not ADOQuery2.Eof do
 begin
    IF ADOQuery2.FieldByName('TYPE').AsString = 'Nil' then
       Begin
TreeView1.Items.AddChildObject(nil,  ADOQuery2.FieldByName('DESCR').AsString,  Pointer(ADOQuery2.FieldByName('IDREC').AsInteger));
       End
    Else
       Begin
TreeView1.Items.AddChildObject(TreeView1.Items[ADOQuery2.FieldByName('TYPE').AsInteger],ADOQuery2.FieldByName('DESCR').AsString,Pointer(ADOQuery2.FieldByName('IDREC').AsInteger));
       End;
   ADOQuery2.Next;
 End;
TreeView1.Items.EndUpdate;


При этом всю основную нагрузку по формированию данных выполняет SQL сервер а Delphi лишь остается нарисовать дерево пройдясь по всем строчкам в возвращенном наборе данных.
Либо можно организовать рекурсивное построение дерева средствами самого Delphi. каму как больше нравится.

Автор: Ddddddelphi 10.12.2004, 23:15
BSV_Sergey, спасибо, но я сделал бд используя BDE,

извиняюсь, что неполностью всё не описал, как с помощью компонентов DataSourse и Query добиться решения задачи?


Автор: SPrograMMer 11.12.2004, 12:36
Ну, берешь, Query, которые выбирает сотрудников первого уровня - саамых главных, - из этого списка, создаешь, узлы первого уровня, затем берешь первый узел, смотришь сотрудников, которые ему подчиняются, - это узелы второго уровня для первого узла, затем, берешь первый узел второго уровня, для первого начальника, смотришь какие люди ему подчиняются,... и т.д.
Вот и построишь дерево.

Автор: BSV_Sergey 16.12.2004, 15:00
Алгоритм формирования дерева можно организовать используя код приведенный ниже. Возможно его удастся улучшить, это первое решение, которое пришло мне в голову;
Структура таблицы используемой в примере:
REPORTTREE
treeid description treeupid
1 tree1 0
2 tree2 1
3 tree3 1 и тд
Текст модуля:
Код

unit Unit1;

interface

uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 Dialogs, DB, ADODB, StdCtrls, ComCtrls;

type
 TForm1 = class(TForm)
   ADOQuery1: TADOQuery;
   TreeView1: TTreeView;
   Button1: TButton;
   procedure Button1Click(Sender: TObject);
 private
   { Private declarations }
 public
   { Public declarations }
   gNOM:integer;
//Глобальный счетчик количества ветвей в дереве
 end;

var
 Form1: TForm1;

procedure treeadd();
Function recursADD(id:string;nomer:integer):Boolean;

implementation

{$R *.dfm}

procedure treeadd();
var     sSQL,treeid:string;
       n,i:integer;

begin
Form1.gNOM := -1;
sSQL := 'select count(*) as counttree from REPORTTREE where treeupid = 0';
with form1.ADOQuery1 do
begin
 Close;
 SQL.Clear;
 SQL.Add(sSQL);
 Open;
end;
treeid := '';
n := Form1.ADOQuery1.FieldByName('counttree').AsInteger;
for i:=1 to n do
begin
 if treeid = '' then
   sSQL := 'Select top 1 * from REPORTTREE where treeupid = 0'
 else
   sSQL := 'Select top 1 * from REPORTTREE where treeupid = 0 and treeid not in ('+treeid+')';
  with form1.ADOQuery1 do
   begin
    Close;
    SQL.Clear;
    SQL.Add(sSQL);
    Open;
   end;
  if treeid = '' then
    treeid :=Form1.ADOQuery1.FieldByName('treeid').Asstring
  else
    treeid := treeid + ','+Form1.ADOQuery1.FieldByName('treeid').Asstring;
   Form1.TreeView1.Items.BeginUpdate;
   Form1.TreeView1.Items.Add(nil,Form1.ADOQuery1.FieldByName('DESCRIPTION').AsString);
   Form1.gNOM := Form1.gNOM + 1;
   recursADD(Form1.ADOQuery1.FieldByName('treeid').AsString,Form1.gNOM);
end;
Form1.TreeView1.Items.EndUpdate;
end;


Function recursADD(id:string;nomer:integer):Boolean;
var     sSQL,treeid:string;
       i,n:integer;
begin
sSQL := 'select count(*) as counttree from REPORTTREE where treeupid = '+id;
with form1.ADOQuery1 do
begin
 Close;
 SQL.Clear;
 SQL.Add(sSQL);
 Open;
end;
treeid := '';
n := Form1.ADOQuery1.FieldByName('counttree').AsInteger;
if n > 0 then
for i:=1 to n do
begin
 if treeid = '' then
   sSQL := 'Select top 1 * from REPORTTREE where treeupid = '+id
 else
   sSQL := 'Select top 1 * from REPORTTREE where treeupid = '+id+' and treeid not in ('+treeid+')';
  with form1.ADOQuery1 do
   begin
    Close;
    SQL.Clear;
    SQL.Add(sSQL);
    Open;
   end;
  if treeid = '' then
    treeid :=Form1.ADOQuery1.FieldByName('treeid').Asstring
  else
   treeid := treeid + ','+Form1.ADOQuery1.FieldByName('treeid').Asstring;
  Form1.TreeView1.Items.AddChild(Form1.TreeView1.Items[nomer],Form1.ADOQuery1.FieldByName('DESCRIPTION').AsString);
  Form1.gNOM := Form1.gNOM + 1;
  recursADD(Form1.ADOQuery1.FieldByName('treeid').AsString,Form1.gNOM);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
treeadd();
end;

end.


Ddddddelphi Надеюсь, я не слишком опоздал с ответом и мое решение smile еще актуальною

Автор: Ddddddelphi 16.12.2004, 15:13
BSV_Sergey,
актуально, я учусь для себя поками-сь....
cпасибо!!!!! поработаю...

глупые вопросы, а:

Структура таблицы используемой в примере:
REPORTTREE
это из стандартных? не нашёл...c этим ладно

treeid description treeupid
1 tree1 0
2 tree2 1
3 tree3 1 и тд
чё воще означает?

Автор: BSV_Sergey 16.12.2004, 17:12
Таблица REPORTTREE - содержит 3 столбца:
treeid - id ветви дерева
description - название ветви дерева
treeupid - какой ветви дерева подчиняется.
В случае если treeupid = 0 - ветвь является контевой ветвью дерева и не кому не подчиняется.
Я назвал таблицу REPORTTREE, в твоей программе она может называться по другому. В твоем примере, если я правильно понял code - это treeid в моей таблице name - description, dopcode - treeupid.
К примеру дерево на основе следующих данных:

treeid description treeupid
1 Иванов 0
2 Петров 1
3 Сидоров 1
4 Смирнов 3
5 Наумов 2

будет выглядеть так
- Иванов
----Петров
-------Наутов
----Сидоров
-------Смирнов

Автор: BSV_Sergey 16.12.2004, 17:48
Я посмотрел твой пример. У тебя есть возможность, что у одного сотрудника могут быть несколько руководителей. В данной ситуации я еще посоветовал бы слегка изменить структуру таблиц, чтобы избежать избыточности данных. Можно создать таблицу с информацией о сотрудниках, к примеру Employee (employeeid(ID сотрудника), lastname, firstname, post и тд) и таблицу Hierarchy (employeeid, chiefid) в которой будет прописана орг структура предприяпия. В поле employeeid будет храниться ID сотрудника из таблицы employee, а в поле chiefid – ID сотрудника из таблица employee, который является руководителем первого.
Example:
Employee
1 ivanov ivan Manager
2 petrov ivan Chief of Branch of Department
3 sidorov petr Director

Hierarchy
3 0
1 3
2 3
1 2

Дерево будет выглядеть так
--sidorov
----ivanov
----petrov
--------ivanov
Хотя мож это и лищнее, смотри сам..smile

Автор: Akella 18.12.2004, 11:09
Еще проще
Ставищ EhLib 3.5, если еще не стоит
В оэтой библиотеке много полезного, в том числе есть и такая фича, которая тебе нужна, по трем полям построить дерево.

Автор: Akella 21.12.2004, 12:57
попытался отсканировать пару страниц из книги "Мир Interbase"
получилось кривовато smile

есть таблица

[ID_GOODS] [ID_PARENT_GOODS] [GOOD_NAME]
1 0 GOODS
2 1 Бытовая техника
3 1 Компьютеры и комплектующие
4 2 Холодиль ники
5 2 Стиральные машины
6 4 Трехкамерные
7 4 Двухкамерные
8 4 Однокамерные
9 5 Вертикальные
10 5 Фронтальные
11 10 Узкие
12 10 Классические

DECLARE VARIABLE CURR_CHILD_NAME VARCHAR(80);
BEGIN
/*Организуем внешний цикл FOR SELECT no непосредственным потомкам товара с ID_GOOD=ID_GOOD2SHOW */
FOR SELECT gt1.id_good, gt1.good_name
FROM GoodsTree gt1
WHERE gt1.id_parent_good=:ID_good2show
INTO:ID_CHILD_G00D, :full_goods_name
DO
BEGIN
/*Проверка с помощью функции EXISTS, которая возвращает
TRUE, если запрос в скобках вернет хотя бы одну строку.
Если у найденного узла с ID_PARENT_GOOD = ID_CHILD_GOOD нет потомков,
то он является "листом" дерева и попадает в результаты */
IF (NOT EXISTS(
SELECT * FROM GoodsTree
WHERE GoodsTree.id_parent_good=:id_child_good))
THEN BEGIN
/* Передаем "лист" дерева в результаты */
SUSPEND;
END
ELSE
/* Для узлов, у которых есть потомки*/
BEGIN
/*сохраняем имя узла-родителя во временной переменной */
CURR_CHILD_NAME=full_goods_name;
/ * рекурсивно запускаем эту процедуру */
FOR
SELECT ID_CHILD_GOOD, full_goods_name
FROM GETFULLNAME (:ID_CHILD_GOOD)
INTO:ID_CHILD_GOOD, :full_goods_name
DO BEGIN
/*добавляем имя узла-родителя к найденному имени потомка с помощью операции конкатенации строк || */
full_goods_name=CURR_CHILD_NAME||' '||full_goods_name;
SUSPEND; /* возвращаем полное имя товара*/
END
END
END
END
Если мы выполним данную процедуру с входным параметром ID_GOOD2SHOW=1, то получим следующее:
FULL_GOODS_NAME FULL_GOODS_NAME
Бытовая техника Холодильники Трехкамерные 6
Бытовая техника Холодильники Двухкамерные 7
Бытовая техника Холодильники Однокамерные 8
и т.д.

Автор: Ddddddelphi 22.12.2004, 19:55
@dsergey
"Мир Interbase"-что за книга? специально для Дельфи или? До изучения Interbase по книге ещё не дошёл, надо будет твой код рассмотреть, когда дойду.

Автор: Akella 23.12.2004, 09:47
"Мир Interbase (вторая редакция)" это, насколько я знаю единственная книга на русском языке по Interbase, для Delphi там не очень много, еще продается с ней компакт-диск, там прилочно информации, компонентов, а также кроме IB7, есть "огненная утка"

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)