查询分层数据并将所有关系置于链中

时间:2014-01-28 22:40:22

标签: sql-server tsql recursion while-loop

我有父/子分层数据,如下所示:

AgencyId  ParentAgencyId
6220        NULL
6221        6220
6222        6221
6223        6221
6224        6223
6219        6220
6225        NULL

我正在尝试查询以抓取层次结构以匹配所有关系并设置"级别"类似于下面。

Parent    Child     Level
6220        6220        0
6220        6221        1
6220        6222        2
6220        6223        2
6220        6224        3
6220        6219        1
6221        6222        1
6221        6223        1
6221        6224        2
6223        6224        1
6225        6225        0

如果代理商没有父母,那么它将具有"等级"为零。

我试图做的代码是:

DECLARE @AgencyCount INT
DECLARE @i INT = 1
DECLARE @AgencyId INT

IF OBJECT_ID('tempdb..#tmpAgencies') > 0
DROP TABLE #tmpAgencies

CREATE TABLE #tmpAgencies (ID INT IDENTITY(1,1), AgencyId INT)
INSERT INTO #tmpAgencies SELECT AgencyId FROM dbo.Agency WHERE CreatorId = 59641 

IF OBJECT_ID('tempdb..#ChildAgencies') > 0
DROP TABLE #ChildAgencies

CREATE TABLE #ChildAgencies(AgencyId INT, ParentAgencyId INT, IsMultiAgencyOffice BIT,     LevelCount INT)  

SELECT @AgencyCount = COUNT(AgencyId) FROM #tmpAgencies         

WHILE @i < @AgencyCount

BEGIN  

    DECLARE @InsertedCount INT
    DECLARE @AgencyLevel INT = 0

    SELECT @AgencyId = AgencyId FROM #tmpAgencies AS TA WHERE ID = @i

    INSERT INTO #ChildAgencies(AgencyId, ParentAgencyId, IsMultiAgencyOffice, LevelCount)
    SELECT AgencyId, @AgencyId, IsMultiAgencyOffice, @AgencyLevel   
    FROM dbo.Agency
    WHERE AgencyId = @AgencyId

    SET @InsertedCount = @@ROWCOUNT

    WHILE @InsertedCount > 0
        BEGIN  

            SET @InsertedCount = NULL
            SET @AgencyLevel = @AgencyLevel + 1      

            INSERT INTO #ChildAgencies(AgencyId, ParentAgencyId, IsMultiAgencyOffice, LevelCount)
            SELECT AgencyId, @AgencyId, IsMultiAgencyOffice, @AgencyLevel
            FROM dbo.Agency AG
            WHERE AgencyId NOT IN (SELECT AgencyId FROM #ChildAgencies)
                AND ParentAgencyId IN (SELECT AgencyId FROM #ChildAgencies)
                AND StatusCode <> 109 /*QA-Deleted*/

            SET @InsertedCount = @@ROWCOUNT


        END  



    SET @i = @i + 1

END

我从其他东西中借用了最内在的循环,这几乎就是我想要的东西。我知道循环不受欢迎。我原本试图通过递归CTE来做到这一点,但无法让它发挥作用。另外,我真的很讨厌where子句中的子查询,但是一旦我开始得到我想要的结果,我就会解决这个问题。

由于

2 个答案:

答案 0 :(得分:1)

在这种情况下通常需要的是:

Parent    Child     Level
6220        6220        0
6220        6221        1
6220        6222        2
6220        6223        2
6220        6224        3
6220        6219        1
6225        6225        0

而不是:

6221        6222        1
6221        6223        1
6221        6224        2
6223        6224        1

您可以使用递归CTE获得第一部分:

with cte as (
      select AgencyId as Parent, AgencyId as Child, 0 as level
      from Agency
      where ParentAgencyId is null
      union all
      select cte.Parent, a.AgencyId, cte.level + 1
      from cte join
           Agency a
           on a.ParentAgencyId = cte.Child
   )
select *
from cte;

Here是一个SQL小提琴,显示它正在运行。

此版本似乎产生了您想要的输出:

with cte as (
      select AgencyId as Parent, AgencyId as Child, 0 as level
      from Agency
      where ParentAgencyId is null
      union all
      select cte.Parent, a.AgencyId, cte.level + 1
      from cte join
           Agency a
           on a.ParentAgencyId = cte.Child
      union all
      select a.ParentAgencyId, a.AgencyId, cte.level + 1
      from cte join
           Agency a
           on a.ParentAgencyId = cte.Child
   )
select distinct *
from cte;

SQL小提琴是here。我从未使用过具有两个union all子句的递归CTE。

编辑:

此版本生成您想要的输出。我似乎过度复杂了上面的问题(但一路上,确实知道递归CTE可以有两个union all子查询):

 cte as (
  select AgencyId as Parent, AgencyId as Child, 0 as level, ParentAgencyId as OriginalPA
  from Agency
  union all
  select cte.Parent, a.AgencyId, cte.level + 1, OriginalPA
  from cte join
       Agency a
       on a.ParentAgencyId = cte.Child
)
select Parent, Child, Level
from cte
where level >  0 or OriginalPA is null;

您可以看到此工作here

答案 1 :(得分:0)

我只想用一个简单的循环来做。

-- load some data into some table 


drop table tbl; 

create table tbl(id int, parentid int, level int);

insert into tbl(id, parentid) values (1, 2);
insert into tbl(id, parentid) values (2, NULL);
insert into tbl(id, parentid) values (3, 1);
insert into tbl(id, parentid) values (4, NULL);
insert into tbl(id, parentid) values (5, NULL);
insert into tbl(id, parentid) values (6, 5);


-- update the levels 

declare @done int;  
declare @lvl int;

set @done = 0;
set @lvl = 0;

update tbl set [level] = 0 where parentid is null;

set @lvl = 0;

while (@done = 0) 

BEGIN

  update t2 
  set t2.[level] = t1.[level] + 1
  from tbl t2 join tbl t1 on t1.[level] = @lvl and t2.parentid = t1.id;

  set @lvl = @lvl + 1;

  if (not (exists (select null from tbl where [level] is null)))
  begin
     set @done = 1
  end

END; 




select * From tbl ;