powered by simpleCommunicator - 2.0.41     © 2025 Programmizd 02
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Как избавиться от функции с иерархическим запросом.
4 сообщений из 4, страница 1 из 1
Как избавиться от функции с иерархическим запросом.
    #37768190
aleksandy
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Есть таблица, ссылающаяся на саму себя и таблицу дополнительных параметров. Требуется достать значение параметра для корня ветки. На всякий случай проиллюстрирую:
Код: plaintext
1.
2.
3.
4.
5.
6.
node-1
 |-node-1-1
   |-node-1-1-1
   |-node-1-1-2
     |-node-1-1-2-1
   |-node-1-1-3
Так вот при передаче id любой из перечисленных нод, нужно вернуть параметр node-1. Я написал для этого функцию, которая работает с приемлимой скоростью. Однако, если её использовать в больших выборках, все начинает тормозить.

Так вот, собственно, можно ли как-то заставить это работать быстрее, или можно как-то по-другому сделать?

Вот набросал тестовый пример. Таблица node наполнена таким количеством данных, которое соответствует реальному количеству данных в боевых таблицах.

Код: 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.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
declare
  not_exists_exception exception;
  pragma exception_init(not_exists_exception, -942);
begin
  begin
    execute immediate 'drop table node';
  exception
    when not_exists_exception then
      null;
  end;
  begin
    execute immediate 'drop table node_param';
  exception
    when not_exists_exception then
      null;
  end;
end;
/

create table node_param as
select 
    level id
  , dbms_random.string('A', trunc(dbms_random.value(1, 15))) val
  from dual
  connect by level <= 999
;
alter table node_param add constraint pk_node_param primary key (id);

create table node as
select 
    t.id
  , case
      when dbms_random.value < .4 
      then t.id
      else trunc(dbms_random.value(1, 99999))
    end node_id
  , trunc(dbms_random.value(1, 999)) param_id
  from (
    select level id
      from dual
      connect by level <= 99999
  ) t;
alter table node add constraint pk_node primary key (id);
alter table node add constraint fk_node_node_id foreign key (node_id) references node (id);
alter table node add constraint fk_node_param_id foreign key (param_id) references node_param (id);

create index idx_node_node_id on node (node_id);
create index idx_node_param_id on node (param_id);

create or replace function get_head_param(
  p_id in number
) return varchar2
is
  m_result varchar2(15);
begin
  select *
    into m_result
    from (
      select tp.val
        from node t
          join node_param tp
            on t.param_id = tp.id
        start with t.id = p_id
        connect by prior decode(t.node_id, t.id, null, t.node_id) = t.id
        order by level desc
    )
    where rownum < 2;
  return m_result;
exception
  when no_data_found then
    null;
end get_head_param;
/



Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
select 
    t.id
  , get_head_param(t.id) tp_val
  from (
    select id, dbms_random.random
      from node
      order by 2
  ) t
  where rownum < 100;


работает ~1 минуты :(. Хотелось бы ускориться хотя бы до ~10-15 секунд.

З.Ы. Используется Oracle 10XE
...
Рейтинг: 0 / 0
Как избавиться от функции с иерархическим запросом.
    #37768334
Фотография suPPLer
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
aleksandy или можно как-то по-другому сделать?
Например:
0. У корней хранить NULL в node_id.
1. В запросе функции соединять с таблицей параметров результат поиска корня:

Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
select tp.val
  into m_result
  from node_param tp
     , (select     t.param_id
              from node t
             where t.node_id is null
        start with t.id = p_id
        connect by prior t.node_id = t.id
        ) r
  where t.param_id = tp.id;


2. Убрать обработку NO_DATA_FOUND в функции. Или хотя бы возвращать NULL.
3. Последний шаг: денормализовать структуру, добавив поле root_id в таблицу nodes. Или создать отдельную таблицу из двух полей (root_id, node_id). При изменении корня не забывать её обновлять.
...
Рейтинг: 0 / 0
Как избавиться от функции с иерархическим запросом.
    #37768367
Фотография andrey_anonymous
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
При заранее известной предельной глубине вложенности можно вообще отказаться от connect by, заменив ссылку ключем классификатора.
...
Рейтинг: 0 / 0
Как избавиться от функции с иерархическим запросом.
    #37769689
aleksandy
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
suPPLer,

0. Уже поздно, слишком много кода, который может поломаться, если в поле будет null. Я знаю, что это нехорошо, но такое вот тяжёлое наследие.
1. А вот за это спасибо, для 1 записи средняя скорость с 2-2.5 секунд возросла до 0.016-0.2
2. В оригинальной функции я, естественно, возвращаю null при отсутствии данных. Чего-то я не забыл про return в тестовом примере. :)
3. Для того чтобы сделать это, всё равно придется искать этот root_id, а это слишком долго, я не могу остановить приложение так надолго. Признаться честно, это была моя первая мысль.

andrey_anonymous,
фишка как раз в том, что уровень вложенности никак не ограничен, и пользователи могут сделать его сколь угодно большим. Хотя в 90% случаев глубина не превышает 3 уровней.

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


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