T-SQL对多列中的每个值执行递归

时间:2017-07-07 20:27:57

标签: sql sql-server tsql recursion stored-procedures

我有一个包含两列的数据集。第一栏中的某个人可能处于第二栏中某个人的控制范围内(即每个人都在迈克尔的控制范围内,德怀特和斯坦利在吉姆的控制范围内):

(node:14312) DeprecationWarning: `open()` is deprecated in mongoose >= 4.11.0, use `openUri()` instead, or set the `useMongoClient` option if using `connect()` or `createConnection()`. See http://mongoosejs.com/docs/connections.html#use-mongo-client
{ _id: 595fe7c14a9810330c75aacc,
  password: '297d5907-d9d7-49ef-800c-97a56aa395f7',
  email: 'test@test.com',
  __v: 0,
  todos: [] }

我有一张表格列出了每个人及其主管:

source_id     source          target_id     target        
1             Michael Scott   5             Kelly Kapoor  
3             Dwight Schrute  2             Jim Halpert
4             Stanley Hudson  2             Jim Halpert
2             Jim Halpert     5             Kelly Kapoor

我有一段代码块,它使用递归来查找上表中单个人的控制范围:

person_id       person           supervisor_id     supervisor
1               Michael Scott    0                 None
2               Jim Halpert      1                 Michael Scott
3               Dwight Schrute   2                 Jim Halpert
4               Stanley Hudson   2                 Jim Halpert
6               Ryan Howard      1                 Michael Scott
5               Kelly Kapoor     6                 Ryan Howard

这段代码可以转换成存储过程或函数。为Jim运行它(例如),返回:

with anchor as
(
    select person_id, supervisor_id from table where unique_id = @ID
    union all
    select a.person_id, a.supervisor_id 
    from table a
    inner join Anchor b ON b.person_id = a.supervisor_id
)

select a.person_id
from anchor a

如何将初始数据集中的每个值(来自源列和目标列)与前一个代码块返回的列中的值进行比较?换句话说,对于person_id 3 (Dwight Schrute) 4 (Stanley Hudson) 中的每一行,我需要检查该名称是否在source的控制范围内。另外,对于target中的每一行,我需要检查该名称是否在target的控制范围内。

期望的最终结果:

source

我知道迭代很糟糕(即使用游标或while循环为每一行运行存储过程)。我也知道source_id source target_id target isEitherPersonInOther'sSOC 1 Michael Scott 5 Kelly Kapoor Yes 3 Dwight Schrute 2 Jim Halpert Yes 4 Stanley Hudson 2 Jim Halpert Yes 2 Jim Halpert 5 Kelly Kapoor No 和一个功能在一起可能有用,但我一直无法解决这个问题。

感谢您有任何见解!

2 个答案:

答案 0 :(得分:0)

您需要的是将递归CTE和输出ID对带入临时表。我想象这样的事情:

DECLARE @id int
DECLARE cx CURSOR FOR 
    SELECT person_id FROM PersonSupervisor

CREATE TABLE #tmpPS (Supervisor int, personInChain int)

OPEN cx
FETCH NEXT FROM cx
    INTO @id

WHILE @@FETCH_STATUS = 0
BEGIN
    with anchor as
    (
        select person_id, supervisor_id from table where unique_id = @ID
        union all
        select a.person_id, a.supervisor_id 
        from table a
        inner join Anchor b ON b.person_id = a.supervisor_id
    )
    INSERT INTO #tmpPS
    select @id, a.person_id
    from anchor a

    FETCH NEXT FROM cx
        INTO @id
END
Close cx
Deallocate cx

这将创建一个包含所有关系的表,以递归方式展开。然后,您可以使用此子查询输出任何给定的人是否高于或低于给定的其他人。将其添加到基础网格的任何查询输出中:

SELECT
    SourceId,
    Source,
    TargetId,
    Target,
    CASE
        WHEN EXISTS (SELECT 1 FROM #tmpPS WHERE Supervisor = SourceId and PersonInChain = TargetId)
            THEN 'Yes'
        WHEN EXISTS (SELECT 1 FROM #tmpPS WHERE Supervisor = TargetId and PersonInChain = SourceId)
            THEN 'Yes'
        ELSE 'No'
    END as [isEitherPersonInOther'sSOC]
FROM ....

这也意味着你可以将这种关系分开的一个版本 - 如果是第一个查询,则TargetId是SourceId的下属。如果是第二个查询,则TargetId优于SourceId。

答案 1 :(得分:0)

必须有更好的方法来做到这一点,但这绝对是一种方法 - 请注意代码创建永久表以在函数中使用:

    create table dbo.[source]
(
    source_id  int,
    [source] nvarchar(500),
    target_id int,
    [target] nvarchar(500)
)

insert into [source]
select 1, 'Michael Scott', 5, 'Kelly Kapoor'
union all select 3,'Dwight Schrute',2,'Jim Halpert'
union all select 4 ,'Stanley Hudson',2,'Jim Halpert'
union all select 2  ,'Jim Halpert',5,'Kelly Kapoor'

create table dbo.supervisors
(
    person_id int,
    person nvarchar(500),
    supervisor_id int,
    supervisor nvarchar(500)
)

insert into dbo.supervisors
select 1,'Michael Scott', 0,'None'
union all select 2,'Jim Halpert',1,'Michael Scott'
union all select 3,'Dwight Schrute',2,'Jim Halpert'
union all select 4,'Stanley Hudson',2,'Jim Halpert'
union all select 6,'Ryan Howard',1,'Michael Scott'
union all select 5 ,'Kelly Kapoor',6,'Ryan Howard'


go


create function dbo.fn_isinspanofcontrol
(
    @sourceid int,
    @targetid int
)
RETURNS varchar(1)

as
begin

declare @retVal varchar(1)
declare @tbl table
(
    person_id int
)

;with anchor as
            (
                select person_id, supervisor_id from supervisors where person_id = @sourceid
                union all
                select a.person_id, a.supervisor_id 
                from supervisors a
                inner join Anchor b ON b.person_id = a.supervisor_id
            )
insert into @tbl
select a.person_id
from anchor a
where   
    a.person_id = @targetid

;with anchor as
            (
                select person_id, supervisor_id from supervisors where person_id = @targetid
                union all
                select a.person_id, a.supervisor_id 
                from supervisors a
                inner join Anchor b ON b.person_id = a.supervisor_id
            )
insert into @tbl
select a.person_id
from anchor a
where   
    a.person_id = @sourceid



if exists( select 1
            from @tbl 
        )
begin
    set @retVal = 'Y'
end
else
begin
    set @retVal = 'N'
end

    return @retVal

end


select 
    *, dbo.fn_isinspanofcontrol(source_id,target_id)
from [source]