我有父/子分层数据,如下所示:
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子句中的子查询,但是一旦我开始得到我想要的结果,我就会解决这个问题。
由于
答案 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 ;