Запрос: интересный иерархический

Averi
Дата: 01.03.2007 15:32:14
Необходимо построить эффективный запрос , возвращающий дерево
(дерево должно строиться ровно один раз).
Нужно вернуть только те ветки деревьев, которые в качестве листьев
имеют значение поля what='item', остальные отбросить.
Данный запрос имеется (см. ниже), но достаточно не эффективный, потому
что приходиться 2 раза строить дерево (перевернуть туда-обратно).

--creating table
CREATE TABLE tmenu
(
id_menu NUMBER,
id_parent NUMBER,
what VARCHAR2(4));

--inserting test data
insert into tmenu (id_menu,id_parent,what)
values (21101,21001,'item');
insert into tmenu (id_menu,id_parent,what)
values (71001,11001,'menu');
insert into tmenu (id_menu,id_parent,what)
values (51001,21001,'item');
insert into tmenu (id_menu,id_parent,what)
values (21001,null,'menu');
insert into tmenu (id_menu,id_parent,what)
values (31001,21001,'item');
insert into tmenu (id_menu,id_parent,what)
values (110001,null,'item');
insert into tmenu (id_menu,id_parent,what)
values (41001,21001,'item');
insert into tmenu (id_menu,id_parent,what)
values (11002,11001,'menu');
insert into tmenu (id_menu,id_parent,what)
values (11001,null,'menu');
insert into tmenu (id_menu,id_parent,what)
values (11201,null,'menu');


--В результате должны получить след:

LEVEL ID_MENU ID_PARENT WHAT
------------------------------------------------------------
1 110001 item
1 21001 menu
2 51001 21001 item
2 21101 21001 item
2 31001 21001 item
2 41001 21001 item

--Есть запрос, для получения для данного результата, но в нем необходимо 2 раза
--строить дерево:


select level, v.id_menu, v.id_parent, what
from
(select distinct * from tmenu
start with what='item'
connect by prior id_parent=id_menu) v
start with id_parent is null
connect by prior id_menu=id_parent
Elic
Дата: 01.03.2007 16:37:24
Averi
Есть запрос, для получения для данного результата, но в нем необходимо 2 раза строить дерево
А почему это плохо? :)
На тебе только одно построение дерева:
with t as
( select level as lvl, id_menu, id_parent, what, sys_connect_by_path(id_menu, '\') as path
    from tmenu
    start with id_parent is null
    connect by id_parent = prior id_menu
)
select distinct t1.lvl, t1.id_menu, t1.id_parent, t1.what
  from t t1, t t2
  where t2.what = 'item'
    and t2.path like t1.path || '%'
;

          LVL       ID_MENU     ID_PARENT WHAT
------------- ------------- ------------- ----
            1         21001               menu
            1        110001               item
            2         21101         21001 item
            2         31001         21001 item
            2         41001         21001 item
            2         51001         21001 item

6 rows selected.
with t as
( select level as lvl, id_menu, id_parent, what, sys_connect_by_path(id_menu, '\') as path, rownum as rn
    from tmenu
    start with id_parent is null
    connect by id_parent = prior id_menu
)
select t1.lvl, t1.id_menu, t1.id_parent, t1.what
  from t t1
  where exists
    ( select null from t t2 
        where t2.what = 'item'
          and t2.path like t1.path || '%'
    )
  order by t1.rn
;

          LVL       ID_MENU     ID_PARENT WHAT
------------- ------------- ------------- ----
            1         21001               menu
            2         21101         21001 item
            2         51001         21001 item
            2         31001         21001 item
            2         41001         21001 item
            1        110001               item

6 rows selected.
Vladimir Sitnikov
Дата: 02.03.2007 02:19:33
Скучно. Стандартный запрос -- стандарное решение :-/
select *
  from (select level as lvl,
               id_menu,
               id_parent,
               what,
               max(what) keep(dense_rank last order by level) over(partition by rownum - level) type
          from tmenu
         start with id_parent is null
        connect by id_parent = prior id_menu)
 where type = 'item'

PS. Для пущей 'оптимизации', следует сделать index on tmenu(decode(id_parent,null,1)) и заменить условие в start with на нормальное

PPS. А ещё лучше, наверное, сделать mview, в котором хранится уже неважно каким путём полученный результат.
Elic
Дата: 02.03.2007 08:48:10
Vladimir Sitnikov
Скучно. Стандартный запрос -- стандарное решение :-/
Тебе рано скучать и называть "стандартным решением" свою поделку, способную разве что продемонстрировать магию исходных данных
Работай над собой:
insert into tmenu (id_menu,id_parent,what) values (21100,21001,'menu');
insert into tmenu (id_menu,id_parent,what) values (21101,21001,'item');
...
insert into tmenu (id_menu,id_parent,what) values (11201,null,'menu');
Vladimir Sitnikov
Дата: 02.03.2007 23:29:17
Elic
Тебе рано скучать и называть "стандартным решением" свою поделку, способную разве что продемонстрировать магию исходных данных
Работай над собой:

Это точно. Как можно было подумать, что rownum-level позволит что-то полезное получить? ;-)

Тем не менее, задача стандартной не перестаёт быть. Она сводится к "нахождению суммы выражения
decode(what, 'item', 1, 0)
по всем дочерним узлам любой вложенности и фильтрации по summ_of_decode>0". Верно?

Считаю разумным не выкладывать решение, до того как оно появится здесь
Elic
Дата: 03.03.2007 14:25:22
Vladimir Sitnikov
Тем не менее, задача стандартной не перестаёт быть. Она сводится к "нахождению суммы выражения decode(what, 'item', 1, 0) по всем дочерним узлам любой вложенности и фильтрации по summ_of_decode>0". Верно?
Она может быть сведена. Не факт, что это вообще нужно.
Пока вужу лишь одну стандартность: твоё пальцегнутие, часто неоправданное.
Stax.
Дата: 03.03.2007 17:28:22
Vladimir Sitnikov

Тем не менее, задача стандартной не перестаёт быть. Она сводится к "нахождению суммы выражения
decode(what, 'item', 1, 0)
по всем дочерним узлам любой вложенности и фильтрации по summ_of_decode>0". Верно?

имхо
неверно
надо еще проверять на CONNECT_BY_ISLEAF
напр для
    LEVEL WHAT                 ID_PARENT   ID_MENU
--------- -------------------- --------- ---------
        1 menu                               21001
        2   item                   21001     21101
        2   item                   21001     51001
        2   item                   21001     31001
        2   item                   21001     41001
        3     Oй!                  41001     77001
        1 item                              110001
        1 menu                               11001
        2   menu                   11001     71001
        2   menu                   11001     11002
        1 menu                               11201
.....
stax
Stax.
Дата: 03.03.2007 17:39:57
Elic
Averi
Есть запрос, для получения для данного результата, но в нем необходимо 2 раза строить дерево
А почему это плохо? :)
На тебе только одно построение дерева:

+ проверте на "не лист"
insert into tmenu (id_menu,id_parent,what)
values(77001,41001,'Oй!')
/
.....
stax
Elic
Дата: 03.03.2007 17:53:51
Stax.
имхо надо еще проверять на CONNECT_BY_ISLEAF
В принципе ты прав. Только опровергающий пример неудачный привёл.
IMO, задачу автора следует понимать как "вернуть только те ветки деревьев, которые хотя бы одним листом имеют значение поля what='item'" или "Не выводить меню без элементов".
Stax.
Elic
На тебе только одно построение дерева:
+ проверте на "не лист"
Да, я тоже это не учёл.
Но, IMHO, это и не надо. Если додумать физическую интерпретацию данных, то станет ясно, что what='item' уже само по себе является призноком листа.
Stax.
Дата: 03.03.2007 18:39:49
почемуто считал листьями
конечные узлы ветви (у которых нет "потомков")

.....
stax