Выполнение рекурсивных хранимых процедур в MYSQL для получения иерархических данных - PullRequest
1 голос
/ 21 октября 2008

У меня есть стол сотрудника, как,
работник ( emp_id int первичный ключ, emp_name varchar (50), mngr_id int)

и здесь mngr_id будет либо нулевым, либо содержать действительный emp_id. Так формируется иерархия сотрудников в организации.

Чтобы обойти всю иерархию, мне пришлось написать рекурсивную хранимую процедуру. (в Oracle это просто, используя CONNECT BY .. НАЧАТЬ С)

Итак, вопрос в том, каково влияние такой хранимой процедуры на производительность, если учесть, что уровень иерархии не будет превышать 10 уровней!

Есть ли другой способ добиться того же?

Ответы [ 3 ]

3 голосов
/ 23 июля 2010

довольно простой итеративный список смежных решений на стороне сервера: http://pastie.org/1056977

delimiter ;

drop procedure if exists employee_hier;

delimiter #

create procedure employee_hier
(
in p_emp_id smallint unsigned
)
begin

declare p_done tinyint unsigned default(0);
declare p_depth smallint unsigned default(0);

create temporary table hier(
 boss_id smallint unsigned, 
 emp_id smallint unsigned, 
 depth smallint unsigned
)engine = memory;

insert into hier values (null, p_emp_id, p_depth);

/* http://dev.mysql.com/doc/refman/5.0/en/temporary-table-problems.html */

create temporary table emps engine=memory select * from hier;

while p_done <> 1 do

    if exists( select 1 from employee e inner join hier on e.boss_id = hier.emp_id and hier.depth = p_depth) then

        insert into hier select e.boss_id, e.emp_id, p_depth + 1 
            from employee e inner join emps on e.boss_id = emps.emp_id and emps.depth = p_depth;

        set p_depth = p_depth + 1;          

        truncate table emps;
        insert into emps select * from hier where depth = p_depth;

    else
        set p_done = 1;
    end if;

end while;

select 
 e.emp_id,
 e.name as emp_name,
 b.emp_id as boss_emp_id,
 b.name as boss_name,
 hier.depth
from 
 hier
inner join employee e on hier.emp_id = e.emp_id
inner join employee b on hier.boss_id = b.emp_id;

drop temporary table if exists hier;
drop temporary table if exists emps;

end #

delimiter ;


call employee_hier(1);
call employee_hier(3);
1 голос
/ 21 октября 2008

Относительно последнего вопроса: в * 1001 есть несколько хороших опций: «Какой самый эффективный / элегантный способ разбить плоский стол на дерево?»

Вам также следует рассмотреть возможность кэширования результата рекурсии в промежуточной таблице. Если вы измените его только при обновлении таблицы иерархии, производительность рекурсии будет незначительной.

EDIT: Лично я бы сделал рекурсию на уровне представления моего приложения, например на веб-сервере. Это обеспечивает большую гибкость по сравнению с тем, чего можно достичь в SQL, и вы также можете использовать кэширование на уровне сеанса или приложения. (Использование предварительно созданной таблицы БД, которая обновляется с помощью триггера, никогда не оставляет вас устаревшим кешем.)

0 голосов
/ 23 июля 2010

Томалак: «... Я бы сделал рекурсию на уровне представления моего приложения ...»

Это будет означать, что каждый раз, когда происходит рекурсия, на сервер базы данных с уровня представления отправляется другой вызов. Это было бы невероятно медленно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...