powered by simpleCommunicator - 2.0.53     © 2025 Programmizd 02
Форумы / Firebird, InterBase [игнор отключен] [закрыт для гостей] / Помогите подсчитать количество
5 сообщений из 5, страница 1 из 1
Помогите подсчитать количество
    #38980651
_Счетовод_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Здравствуйте! Требуется помощь в решении такой проблемки:
Есть 2 таблицы:
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
/* Объекты, дерево обычное ... */
CREATE TABLE OBJECTS (
    ID         INTEGER NOT NULL,
    PID        INTEGER
    ...
);
ALTER TABLE OBJECTS ADD CONSTRAINT PK_OBJECTS PRIMARY KEY (ID);
ALTER TABLE OBJECTS ADD CONSTRAINT FK_OBJECTS_1 FOREIGN KEY (PID) REFERENCES OBJECTS (ID) ON DELETE CASCADE ON UPDATE CASCADE;

INSERT INTO OBJECTS VALUES(1, NIL); 
INSERT INTO OBJECTS VALUES(2, NIL); 
INSERT INTO OBJECTS VALUES(3, NIL); 
INSERT INTO OBJECTS VALUES(4, 3); 
INSERT INTO OBJECTS VALUES(5, 4); 

/* ... и расстояния между ними */ 
CREATE TABLE DISTANCES (
    ID         INTEGER NOT NULL,
    IDFROM     INTEGER,
    IDTO       INTEGER
    ...
);
ALTER TABLE DISTANCES ADD CONSTRAINT PK_DISTANCES PRIMARY KEY (ID);

INSERT INTO DISTANCES VALUES(1, 1, 3); 
INSERT INTO DISTANCES VALUES(2, 2, 3); 
INSERT INTO DISTANCES VALUES(3, 3, 4); 
INSERT INTO DISTANCES VALUES(4, 3, 5); 


Мне нужно найти количество дочерних объектов и количество расстояний между ними.
ОК, пишу процедуру:
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
create or alter procedure GET_CD_CNT(
    PARNT integer)
returns (
    OCHILDS integer,
    ODISTS integer)
as
declare variable ID integer;
declare variable VCHILDS integer;
declare variable VDISTS integer;
begin

  PARNT = COALESCE(PARNT,  0);
  OCHILDS = 0;
  ODISTS  = 0;

  SELECT COUNT (1) FROM DISTANCES D WHERE (D.IDFROM = :ROOT) OR (D.IDTO = :PARNT) into :ODISTS;

-- INSERT INTO GTT_DIST 
--   SELECT D.ID FROM DISTANCES D WHERE (D.IDFROM = :PARNT) OR (D.IDTO = :PARNT) 

  for SELECT R.ID FROM OBJECTS R WHERE COALESCE(R.PID,  0) = :ROOT into :ID
  do
  begin
    SELECT G.OCHILDS, G.ODISTS FROM GET_CD_CNT(:ID) G into :VCHILDS, :VDISTS;
    OCHILDS = OCHILDS + VCHILDS + 1;
    ODISTS  = ODISTS  + VDISTS;
  end
  suspend;
end


Подсчет дочерних объектов выполняется нормально, а вот при подсчете расстояний происходит двойной учет расстояний между родителем и наследниками.
(Т.е. дважды учтутся расстояния с ID 3 и 4)
Пока решил это так:
создал временную табличку GTT_DIST, в которую записываю ID расстояний (см. закомментаренные строки в процедуре), а после ее выполнения делаю
Код: plsql
1.
SELECT COUNT DISTINCT(G.ID) FROM GTT_DIST G

и все в норме.
Но терзают смутные сомненья (с), что можно как-то проще, изящнее. Вот прошу уважаемое сообщество подсказать, в каком направлении искать альтернативное решение.
...
Рейтинг: 0 / 0
Помогите подсчитать количество
    #38980671
_Счетовод_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Да, забыл указать: Firebird 2.5.3
...
Рейтинг: 0 / 0
Помогите подсчитать количество
    #38980688
shaposh
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Процедура, возвращающая количество уровней между объектами дерева
DIFF_LEVEL
-1 -Не потомок
-2 -Зацикливание в дереве
Иначе - количество уровней
На примере таблицы BOOK с полями Id, Parent

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
create or alter procedure SYS$BOOK_IS_CHILD (
    OBJ_CHILD_ID integer,
    OBJ_PARENT_ID integer)
returns (
    DIFF_LEVEL integer)
as
declare variable CRASH_COUNT integer;
declare variable FLAG integer;
declare variable REC_ID integer;
declare variable REC_PARENT_ID integer;
begin
  crash_count=1000;

  if (obj_child_id=obj_parent_id)
    then begin diff_level = 0; suspend; exit; end

  flag = 0; diff_level = 0;
  select parent from book where id = :obj_child_id into :rec_parent_id;
  while (rec_parent_id <>0) do
  begin
    diff_level = diff_level + 1;
    if (diff_level > crash_count)
    then
      begin
        flag =-2;
        exit;
      end

    select id, parent from book where id = :rec_parent_id into :rec_id, :rec_parent_id;
    if (:rec_id = obj_parent_id) then begin flag = 1; break; end
  end
  if (flag = 0) then diff_level = -1;
  if (flag = -2) then diff_level = -2;
  suspend;
end
...
Рейтинг: 0 / 0
Помогите подсчитать количество
    #38980725
Фотография Tonal
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Количество детей:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
with recursive
TREE as (
  select ID, PID from OBJECTS where PID = :PID
  union all
  select ID, PID from OBJECTS O
  inner join TREE T on O.PID = T.ID
)
select count(1) from TREE


Насчёт количества расстояний не понятно.
Возможно несколько вариантов:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
-- Все расстояния в которых упомянуты объекты из ветки
with recursive
TREE as (
  select ID, PID from OBJECTS where PID = :PID
  union all
  select ID, PID from OBJECTS O
  inner join TREE T on O.PID = T.ID
)
select count(distinct D.ID) from DISTANCES D
inner join TREE T on D.IDFROM = T.ID or D.IDTO = T.ID

-- Все расстояния в между объектами из ветки
with recursive
TREE as (
  select ID, PID from OBJECTS where PID = :PID
  union all
  select ID, PID from OBJECTS O
  inner join TREE T on O.PID = T.ID
)
select count(distinct D.ID) from DISTANCES D
inner join TREE TF on D.IDFROM = TF.ID
inner join TREE TT or D.IDTO = TT.ID
...
Рейтинг: 0 / 0
Помогите подсчитать количество
    #38980773
_Счетовод_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Tonal,
Да, похоже первый вариант - то что нужно. Только мне нужно получить количество расстояний между объектами, включая родителя:
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
with recursive
TREE as (
  select ID, PID from OBJECTS where ID = :PID -- PID = :PID 
  union all
  select ID, PID from OBJECTS O
  inner join TREE T on O.PID = T.ID
)
select count(distinct D.ID) from DISTANCES D
inner join TREE T on D.IDFROM = T.ID or D.IDTO = T.ID


Спасибо.
...
Рейтинг: 0 / 0
5 сообщений из 5, страница 1 из 1
Форумы / Firebird, InterBase [игнор отключен] [закрыт для гостей] / Помогите подсчитать количество
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]