我如何从自我参考表中找到父母和孩子。

时间:2015-04-06 10:21:11

标签: sql postgresql

这是我的表格(emp_details)内容。 emp_id是"主键"和emp_man_id是"外键"引用emp_detais(emp_id)

自我参考表:

emp_id    emp_name  emp_place  emp_man_name    emp_man_id
    1     arun      klm        arun  
    2     nivin     thr        arun             1
    3     vinay     ekm        arun             1
    4     ajo       plk        nivin            2
    5     alan      knr        nivin            2
    6     ravi      tvm        vinay            3
    7     vipin     mlp        vinay            3
    8     ani       ksd        ajo              4
    9     vino      pta        ajo              4
    11    sarat     wyd        alan             5
    10    siva      alp        alan             5

如果我将值10作为员工ID传递,我需要为此表编写函数;

我需要输出

emp_id emp_man_id
  10         5
  5          2
  2          1

这是我的功能:

CREATE OR REPLACE FUNCTION emp_e1(IN id integer) 
  RETURNS TABLE(em_id integer, emp_mid integer) 
AS 
$BODY$ 
begin 

  return query 
    with recursive d as ( 
       select emp_id, emp_man_id, 0 as level 
       from emp_details 
       where emp_id = id 
       UNION ALL 
       select c.emp_id, c.emp_man_id, level+1 
       from d 
         inner join emp_details c on c.emp_man_id = d.emp_id 
    ) 
    SELECT * 
    FROM d; 
  end; 
$BODY$ 
LANGUAGE plpgsql;

3 个答案:

答案 0 :(得分:3)

编辑:回答是在编辑问题并添加postgresql标记之前,在第一个问题的评论中SQL Server已被提及(我不确定)也许我没有注意到,我错了),这就是为什么答案首先提供给MS SQL Server但是在编辑问题后立即添加查询的postgresql形式并向其添加postgresql标签。)


(对于MS SQL Server)

使用recursive cte

CREATE FUNCTION dbo.fn(@empId int)
RETURNS @t table (empid int, manid int)
as
begin

with cte (empid, manid) as
(
  select emp_id,man_id
  from emp where emp_id=@empId
  union all
  select e.emp_id, e.man_id
  from emp e 
  join cte on e.emp_id=cte.manid
)
insert into @t
select * from cte;
return
end

SQLFIDDLE DEMO


(对于Postgresql)

PostgreSQL只需在recursive之前添加cte关键字,然后将函数格式更改为Posgres格式。

with recursive cte (empid, manid) as
(
  select emp_id,man_id
  from emp where emp_id=10
  union all
  select e.emp_id, e.man_id
  from emp e 
  join cte on e.emp_id=cte.manid
)
select * from cte

SQLFIDDLE DEMO

答案 1 :(得分:0)

使用递归CTE:

DECLARE @t TABLE
    (
      emp_id INT ,
      emp_man_id INT
    )

INSERT  INTO @t
VALUES  ( 1, NULL ),
        ( 2, 1 ),
        ( 3, 1 ),
        ( 4, 2 ),
        ( 5, 2 ),
        ( 6, 3 ),
        ( 7, 3 ),
        ( 8, 4 ),
        ( 9, 4 ),
        ( 11, 5 ),
        ( 10, 5 );
WITH    cte
          AS ( SELECT   emp_id ,
                        emp_man_id
               FROM     @t
               WHERE    emp_id = 10
               UNION ALL
               SELECT   t.emp_id ,
                        t.emp_man_id
               FROM     @t t
                        JOIN cte c ON t.emp_id = c.emp_man_id
               WHERE    t.emp_man_id IS NOT NULL
             )
    SELECT  *
    FROM    cte

输出:

emp_id  emp_man_id
10      5
5       2
2       1

包装功能:

CREATE FUNCTION dbo.Test ( @id INT )
RETURNS @t TABLE
    (
      emp_id INT ,
      emp_man_id INT
    )
AS
    BEGIN
        WITH    cte
                  AS ( SELECT   emp_id ,
                                emp_man_id
                       FROM     TableName
                       WHERE    emp_id = @id
                       UNION ALL
                       SELECT   t.emp_id ,
                                t.emp_man_id
                       FROM     TableName t
                                JOIN cte c ON t.emp_id = c.emp_man_id
                       WHERE    t.emp_man_id IS NOT NULL
                     )
            INSERT  INTO @t
                    ( emp_id, emp_man_id )
                    SELECT  emp_id ,
                            emp_man_id
                    FROM    cte

        RETURN    
    END  

答案 2 :(得分:0)

如果您需要经理的数据,我建议您使用此解决方案(在 SQL Server 中):

  1. 创建一个函数来收集父路径,如路径(..\..\..\
  2. CREATE FUNCTION dbo.AllParents ( @emp_id int )
    RETURNS varchar(max)
    AS
    BEGIN
        Declare @result varchar(max), @emp_man_id int
    
        Set @emp_man_id = (Select emp_man_id from temptable where emp_id = @emp_id)
    
        --if you need also a function for finding children it will be same:
        --Set @emp_man_id = (Select emp_id from temptable where emp_man_id = @emp_id)
    
        if (@emp_man_id is Null)
            set @result = ''
        else
            set @result = dbo.AllParents(@emp_man_id) + '\' + cast(@emp_man_id as varchar(max)) + '#'
    
        return @result
    END
    GO
    
    1. 使用它来获得这样的结果(10的示例):
    2. select * from temptable
      where dbo.AllParents(10) like '%\' + cast(emp_id as varchar(max)) + '#%'
      

      但上面的查询结果并不是你所展示的,但我认为是你想要的 - 这是所有经理人的最高级别的所有数据 - 。

      如果您还希望获得emp_id = 10的数据,可以将其添加到以下位置:

      or emp_id = 10
      

      如果您还添加了一个子功能,请将其添加到这样的

      or dbo.AllChildren(10) like '%\' + cast(emp_id as varchar(max)) + '#%'
      

      注意:对于排序管理员,您还可以使用order by下面的

      order by len(dbo.AllParents(emp_id))
      

      注意:如果您还需要显示路径,请将该功能用作以下字段:

      select *, replace(dbo.AllParents(emp_id), '#', '') from temptable