oracle中的递归函数?

Recursive function in oracle?

我不知道我应该如何在 oracle 中实现它,它对我来说就像递归。 我有这个 table

 |     EMPLOYEES   |
 -------------------
 |   employee_id   |
 -------------------
 |    manager_id   |
 ___________________

并且我想获取特定员工的所有直接和间接下属的数量(将其 ID 作为参数)。更具体地说,每个员工(1除外)都有一个经理,但是,x可能是员工b的经理,而员工b可能是员工[=15]的经理=],所以c也是x的从属。到目前为止,我所能想到的只是 employee_id 的列表和批量收集下属的第一“波”

CREATE OR REPLACE TYPE subordinates AS VARRAY(10) OF NUMBER(4);
/
DECLARE list subordinates;


CREATE OR REPLACE FUNCTION f_sub(v_id employees.employee_id%type) RETURN NUMBER IS 
       
        e_count number;
        
       
                            
BEGIN
    SELECT employee_id BULK  COLLECT INTO list
    FROM EMPLOYEES
    WHERE manager_id = v_id;
    return list.count;

end;

您可以使用这样的查询来获取直接或间接向经理报告的所有员工的数量:

    SELECT COUNT (*) - 1     AS number_of_subordinates
      FROM EMPLOYEES
CONNECT BY PRIOR employee_id = manager_id
START WITH employee_id = v_id;

查询需要从 COUNT(*) 中减去 1,因为查询将 return 您传递的 employee_id 作为查询的行之一。

此查询将return...

  • -1 如果 v_id (employee_id) 不存在
  • 0 如果 v_id 存在,但没有向该员工报告的员工
  • 1+ 向该员工报告的员工人数

您确实应该使用分层查询或递归子查询分解,但是...

i'm just curious what it would look like as a function

所以这里有两种使用递归函数的方法。第一个使用查询获取指定 ID 的直接下属的计数,并对每个下属 ID 递归调用该函数,对结果求和:

create or replace function count_subordinates (v_id employees.employee_id%type)
return number
is
  l_count number;
begin
  select count(*) + coalesce(sum(count_subordinates(employee_id)), 0)
  into l_count
  from employees
  where manager_id = v_id;

  return l_count;
end;
/

第二种使用游标循环获取所有从属ID,每次循环将计数器递增1,并直接从PL/SQL(而不是从SQL 像第一个一样查询):

create or replace function count_subordinates (v_id employees.employee_id%type)
return number
is
  l_count number := 0;
begin
  for e in (
      select employee_id
      from employees
      where manager_id = v_id
  )
  loop
    l_count := l_count + 1;
    l_count := l_count + count_subordinates(e.employee_id);
  end loop;

  return l_count;
end;
/

db<>fiddle 带有一些基本的示例数据,显示了调用具有各种起始ID的函数的结果;我已经包含了 EJ Egyed 的分层版本,以表明它们得到了相同的结果(除了不存在的起始 ID,returns 在我的中为零 - 当然,如果需要,您可以更改它。)

但是除非您参加的测试特别要求递归函数,否则我想他们会期望使用分层查询方法。或者可能是递归子查询分解,你可以这样做:

with rcte (employee_id) as (
  select employee_id
  from employees
  where manager_id = 1 -- starting ID
  union all
  select e.employee_id
  from rcte r
  join employees e on e.manager_id = r.employee_id
)
select count(employee_id)
from rcte;

db<>fiddle 最后添加了该方法。