我为这个模糊的标题道歉。我想不出如何最好地总结这个问题。我有一个分层表(例如,ID int, ParentID int
),需要为ID
生成一个子树。这通过递归CTE来完成。困难在于,对于每个节点,我需要计算一组相应值的运行按位OR,然后使用与父节点相同的值得到的bit-OR。这意味着每个节点都继承其父节点的位掩码,并可以设置自己的附加位。我可以使用OUTER APPLY
和我要求的earlier question中提到的技术在CTE的锚成员中计算此值。不幸的是,我无法在CTE的递归部分以相同的方式计算它,因为它使用SUM
并且不允许聚合。
有没有办法重组这个以做我想做的事情?
declare @ID int
set @ID = 1
;with _Bits_(RowNum, BitMask) as
(
select
1,
1
union all select
RowNum + 1,
BitMask * 2
from
_bits_
where
RowNum < 31
),
_Tree_ as
(
select
a.ID,
a.ParentID,
b.BitMask
from
Tree a
outer apply
(
select
sum(distinct y.BitMask) as BitMask
from
BitValues x
inner join _Bits_ y
on (x.Value & y.BitMask) <> 0
where
x.ID = a.ID
) b
where
a.ID = @ID
union all select
a.ID,
a.ParentID,
c.BitMask | b.BitMask
from
Tree a
inner join _Tree_ b
on b.ID = a.ParentID
outer apply
(
select
sum(distinct y.BitMask) as BitMask
from
BitValues x
inner join _Bits_ y
on (x.Value & y.BitMask) <> 0
where
x.ID = a.ID
) c
)
select * from _Tree_
如果它有助于概念化问题:层次结构很像目录结构,并且位掩码就像从父文件夹继承的权限。
create table Tree (ID int primary key, ParentID int null foreign key references Tree (ID))
insert Tree values (1, null)
insert Tree values (2, 1)
insert Tree values (3, 1)
create table BitValues (ID int not null foreign key references Tree (ID), BitMask int not null)
insert BitValues values (1, 1)
insert BitValues values (2, 2)
insert BitValues values (2, 4)
insert BitValues values (3, 8)
insert BitValues values (3, 16)
insert BitValues values (3, 32)
对于@ID
1,我希望查询返回:
+----+----------+---------+ | ID | ParentID | BitMask | +----+----------+---------+ | 1 | NULL | 1 | | 2 | 1 | 7 | | 3 | 1 | 57 | +----+----------+---------+
答案 0 :(得分:0)
declare @ID int;
set @ID = 1;
with extrarows as
(
select t.id, null as parent, v.BitMask as total
from tree t
join BitValues v on t.id = v.id
where t.id = @ID
union all
select t.id, r.id, v.BitMask | r.total
from extrarows r
join Tree t on r.id = t.parentid
join BitValues v on t.id = v.id
)
select id, parent,
MAX(total & 1) +
MAX(total & 2) +
MAX(total & 4) +
MAX(total & 8) +
MAX(total & 16) +
MAX(total & 32) +
MAX(total & 128) +
MAX(total & 256) +
MAX(total & 512) +
MAX(total & 1024) +
MAX(total & 2048) -- more if you want em.
as BitMask
from extrarows
group by id, parent
一些注意事项:
我假设传入的@id是树的“根”。 (如果这不符合您的需要,请随意爬上树以找到根的起始位掩码。)
虽然对MAX
位进行求和确实有效,但对于许多记录中的大位字符串可能不具备高效性。我不知道你有多少比特但它不到16左右应该没问题 - 比如听听你的发现。
要提高性能,请切换到自定义C#聚合。
答案 1 :(得分:0)
Hogan答案的细微改进(IMO):
declare @ID int;
set @ID = 1;
with _Bits_(RowNum, BitMask) as
(
select
1,
1
union all select
RowNum + 1,
BitMask * 2
from
_bits_
where
RowNum < 31
),
extrarows as
(
select t.id, null as parent, v.BitMask as total
from tree t
join BitValues v on t.id = v.id
where t.id = @ID
union all
select t.id, r.id, v.BitMask | r.total
from extrarows r
join Tree t on r.id = t.parentid
join BitValues v on t.id = v.id
)
select a.id, a.parent, sum(distinct y.BitMask) as BitMask
from extrarows a
inner join _Bits_ y
on (a.total & y.BitMask) <> 0
group by a.id, a.parent