2013-05-21 49 views
3

我不知道什么最好的方法是获得我要找的结果。在一个oracle 10g数据库中,我试图查询所有用户,并为每个有主管人员的行生成一个计算字段,如果他们的主管类型是U.如果不是,我需要走到树上直到找到一个。这个查询可能需要递增多个级别。我需要一个递归查询来获得在oracle中的计算字段

所以对于一个Employee表是这样的:

Employee Table 
+-------+----------+--------------+---------------+ 
| empno | empgroup |  user  | supervisor_no | 
+-------+----------+--------------+---------------+ 
|  1 | E  | Joe Schmo |    4 | 
|  2 | E  | Mark Marin |    3 | 
|  3 | U  | Reed Patter |    7 | 
|  4 | E  | Martin Price |    7 | 
|  7 | U  | Mary Wengert |    | 
+-------+----------+--------------+---------------+ 

我希望看到这样的结果,其中manager_no将计算出的字段

Results 
+-------+----------+--------------+---------------+------------+ 
| empno | empgroup |  user  | supervisor_no | Manager_No | 
+-------+----------+--------------+---------------+------------+ 
|  1 | E  | Joe Schmo |    4 |   7 | 
|  2 | E  | Mark Marin |    3 |   3 | 
|  3 | U  | Reed Patter |    7 |   7 | 
|  4 | E  | Martin Price |    7 |   7 | 
|  7 | U  | Mary Wengert |    |   | 
+-------+----------+--------------+---------------+------------+ 

好了,所以我一直在问我试过。我并不是说这是必须这样做的方式,如果有人有更好的建议,我全都是耳朵,但这是我想要实现的。

我想象了两部分。

select * from employee em 
connect by prior supervisor_no = empno 
start with empno = 1 

[结果] [2]:即得到所有我的结果

select em.empno, em.empgroup, em.user, em.supervisor, (my subquery) as manager_no 
from employee em 

查询主查询

| EMPNO | EMPGROUP |  USERNAME | SUPERVISOR_NO | 
--------------------------------------------------- 
|  1 |  E | Joe Schmo |    4 | 
|  4 |  E | Martin Price |    7 | 
|  7 |  U | Mary Wengert |  (null) | 

好吧,我找到了一种方法来筛选对可能工作的团队,但不知道这是否是最有效的路线。

SELECT empno FROM (
SELECT empno FROM employee em 
CONNECT BY PRIOR supervisor_no = empno 
START WITH empno = 1 
order by level) d 
WHERE d.empgroup = 'U' and rownum =1 

我也创建了一个小提琴,如果有帮助。 http://www.sqlfiddle.com/#!4/c8805/4

+0

公平问题。我不确定这是否是我需要的,但我确实需要不断询问每个人的主管,直到获得符合我标准的人员。我会在我能尽快尝试的问题上添加更多信息。谢谢。 – Josh

+0

为你的SQL小提琴+1。 – jpmc26

回答

2

那么,你的问题变得对我是个挑战,但我没有放弃,并得到这个只用SQL和内置函数(不应该慢慢地工作,而且相当可转位需要):

select distinct 
    empno, 
    empgroup, 
    username, 
    supervisor_no, 
    manager_no 
from (
    select 
      e.*, 
      decode(
     instr(
      sys_connect_by_path(empgroup, '/'), 
      'U/', 
      -1 
     ), 
     0, 
     null, 
     substr(
      sys_connect_by_path(empno, '/'), 
      instr(
       sys_connect_by_path(empno, '/'), 
       '/', 
       1, 
       length(
        substr(
         sys_connect_by_path(empgroup, '/'), 
         1, 
         instr(
          sys_connect_by_path(empgroup, '/'), 
          'U/', 
          -1 
         ) 
        ) 
       ) - length(
        replace(
         substr(
          sys_connect_by_path(empgroup, '/'), 
          1, 
          instr(
           sys_connect_by_path(empgroup, '/'), 
           'U/', 
           -1 
          ) 
         ), 
         '/' 
        ) 
       ) 
      ) + 1, 
      instr(
       sys_connect_by_path(empno, '/'), 
       '/', 
       1, 
       length(
        substr(
         sys_connect_by_path(empgroup, '/'), 
         1, 
         instr(
          sys_connect_by_path(empgroup, '/'), 
          'U/', 
          -1 
         ) 
        ) 
       ) - length(
        replace(
         substr(
          sys_connect_by_path(empgroup, '/'), 
          1, 
          instr(
           sys_connect_by_path(empgroup, '/'), 
           'U/', 
           -1 
          ) 
              ), 
               '/' 
             ) 
           ) + 1 
         ) - instr(
           sys_connect_by_path(empno, '/'), 
           '/', 
           1, 
           length(
             substr(
               sys_connect_by_path(empgroup, '/'), 
               1, 
               instr(
                 sys_connect_by_path(empgroup, '/'), 
                 'U/', 
                 -1 
               ) 
             ) 
           ) - length(
             replace(
               substr(
                 sys_connect_by_path(empgroup, '/'), 
                 1, 
                 instr(
                   sys_connect_by_path(empgroup, '/'), 
                   'U/', 
                   -1 
                 ) 
               ), 
               '/' 
             ) 
           ) 
         ) - 1 
       ) 
     ) manager_no 
    from employee e 
    connect by prior empno = supervisor_no 
) 
where manager_no is not null or supervisor_no is null 
order by empno; 

的SQL捣鼓查询:http://www.sqlfiddle.com/#!4/c8805/27/0

UPDATE: 当我早上起床,我意识到一切都可以做更容易和查询变得更具可读性,在这里你去:

select 
    empno, 
    empgroup, 
    username, 
    supervisor_no, 
    null manager_no 
from 
    employee 
where 
    supervisor_no is null 
union all 
select 
    empno, 
    empgroup, 
    username, 
    supervisor_no, 
    substr(ep, 2, instr(ep, '/', 2)-2) manager_no 
from (
    select 
    sys_connect_by_path(empgroup, '/') gp, 
    sys_connect_by_path(empno, '/') ep, 
    e.* 
    from employee e 
    connect by prior empno = supervisor_no 
) e 
where 
    substr(gp, 1, 3) = '/U/' 
    and (length(gp) - length(replace(gp, 'U/'))) = length('U/') 
order by empno; 

它的工作原理!以及上面的查询。SQL小提琴吧:http://www.sqlfiddle.com/#!4/c8805/54/0

享受!

+0

Yahoo,针对所需组的每个名称以及员工的任何ID长度完成。多么迷人的任务!试图正确地为您设置格式,以便消除它的含义。现在回到工作.. – ZZa

+1

你认为你可以总结这是做什么?我很难看这个,但结果看起来不错。 – Josh

+0

诀窍是使用函数'sys_connect_by_path',它为您提供从根节点到节点的路径字符串。因此,在得到它之后,我分析给出的'sys_connect_by_path(empgroup,'/')'的字符串,在这个级别上我首先遇到'U /'(它表示最近的父代不是'E'),然后尝试从'sys_connect_by_path(empno,'/')'中的相同级别提取必要的ID。所以,主要是使用字符串。 – ZZa

0

也许创建一个递归函数来获取用户的'U'型监督员?

CREATE OR REPLACE FUNCTION GET_U_SUPERVISOR(employee_no INT) 
RETURN INT IS 
    supervisor_empno INT; 
    supervisor_group VARCHAR(20); 
BEGIN 
    SELECT empno, empgroup 
    INTO supervisor_empno, supervisor_group 
    FROM employee 
    WHERE employee.empno = (SELECT supervisor_no FROM employee WHERE empno = employee_no); 

    IF 'U' = supervisor_group 
    THEN 
     RETURN supervisor_empno; 
    ELSE 
     RETURN GET_U_SUPERVISOR(supervisor_empno); 
    END IF; 
END GET_U_SUPERVISOR; 
/

然后:

SELECT empno, empgroup, username, supervisor_no, GET_U_SUPERVISOR(empno) 
FROM employee; 

一个SQL小提琴回你:http://www.sqlfiddle.com/#!4/c7540/1/0

我不能保证在任何的表现,不过,如果它是一个大桌子。它是递归的,并将在每行的基础上调用。

+0

哦,天哪,递归地工作PL/SQL函数..对于这样一个任务,只是一个资源杀手,老兄! – ZZa

+0

我很感激你花时间做这个,但我想避免创建自定义函数,因为我没有权利在我正在使用的数据库。 – Josh

+0

哦,甲骨文。我如何讨厌你的权限。 @ ZZa我从未承诺过高性能。我甚至警告过它。但有时候,你并不需要高性能。 OP没有给我们说明数据的大小。我只是试图给出一个工作解决方案。我发现downvote没有要求。 – jpmc26

-1

我递归,SQL Fiddle使用,

CREATE TABLE EMPLOYEE(NO INT, EMP_GROUP VARCHAR2(1), EUSER VARCHAR2(30), SUPERVISOR_NO INT); 

INSERT INTO EMPLOYEE VALUES(1, 'E', 'JOE SCHMO', 4); 
INSERT INTO EMPLOYEE VALUES(2, 'E', 'Mark Marin', 3); 
INSERT INTO EMPLOYEE VALUES(3, 'U', 'Reed Patter', 7); 
INSERT INTO EMPLOYEE VALUES(4, 'E', 'Martin Price ', 7); 
INSERT INTO EMPLOYEE(NO, EMP_GROUP, EUSER) VALUES(7, 'U', 'Mary Wengert'); 
WITH A(EMPLOYEE, MANAGER) AS 
(
    SELECT NO AS EMPLOYEE, SUPERVISOR_NO AS MANAGER FROM EMPLOYEE 
    UNION ALL 
    SELECT A.EMPLOYEE, E.SUPERVISOR_NO AS MANAGER 
    FROM A, EMPLOYEE E 
    WHERE A.MANAGER = E.NO 
    AND E.SUPERVISOR_NO IS NOT NULL 
) 

SELECT * FROM A 
ORDER BY 1 
+0

您的查询会产生两个错误结果的额外行。在你的SQL小提琴上,第一行将员工'1'映射到主管'4'(他不是'U''管理员),最后一行将员工'2'映射到主管'7'(谁不是'2'下的最低等级'U'型经理)。 – jpmc26

+0

即使它工作正常,想象一下,如果最近的经理是3-4级更深。 – ZZa

+0

是的。但我用递归来证明我们可以找到immidiate主管。 – Santhosh