问题描述
我下面的表格帽子包含具有层次结构的数据结构
+----+----------+-------------+
| ID | ParentID | FullPath |
+----+----------+-------------+
| 1 | NULL | (1) |
| 2 | 1 | (1)/(2) |
| 3 | 2 | (1)/(2)/(3) |
| 4 | NULL | (4) |
| 5 | 4 | (4)/(5) |
| 6 | 4 | (4)/(6) |
| 7 | 6 | (4)/(6)/(7) |
+----+----------+-------------+
如何检索某项的祖先? 例如,如果我寻找ID 3的祖先,我将得到1和2。 同样,如果我要查找7,我将得到4和6(注意5不在)。
我知道我可以通过解析列和使用动态SQL来避免在FullPath中使用CTE,但是我在创建查询时遇到了困难。
编辑: 我想要一个查询,使我能够获得项目的每个祖先行。例如,如果我希望祖先为7,则查询将返回下表:
+----+----------+-------------+
| ID | ParentID | FullPath |
+----+----------+-------------+
| 4 | NULL | (4) |
| 6 | 4 | (4)/(6) |
+----+----------+-------------+
原因是因为我有更多描述项目的列,所以我需要获取它们并进行比较。
解决方法
如果您已经拥有完整路径作为数据的一部分,为什么不从中解析出祖先呢? var whereObj = {EMPRESA: "EMPRESA='DEMO'",QTD: "QTD IS NOT NULL "," ": "OR MONTANTE IS NOT NULL"};
var where = "";
var first_loop = true;
for (const [key,value] of Object.entries(whereObj)) {
if(key.trim()){
if(!first_loop) where += " AND";
where += " " + value;
}else{
where += " " + value;
}
first_loop = false;
}
console.log(where);
不需要动态SQL或CTE。
样本数据
string_split()
解决方案
declare @Data table
(
ID int,ParentID int,FullPath nvarchar(50)
);
insert into @Data (ID,ParentID,FullPath) values
(1,NULL,'(1)' ),(2,1,'(1)/(2)' ),(3,2,'(1)/(2)/(3)' ),(4,'(4)' ),(5,4,'(4)/(5)' ),(6,'(4)/(6)' ),(7,6,'(4)/(6)/(7)' );
结果
select d.ID as SelectedID,da.*
from @Data d
cross apply string_split(d.FullPath,'/') s
join @Data da -- data ancestor
on da.ID = convert(int,replace(replace(s.value,'(',''),')',''))
where d.ID = 7
and d.ID <> da.ID -- filter out ID itself
order by d.ID,da.ID;
,
这可以使用自联接来完成:-
DECLARE @Hierarchy TABLE
(
ID int PRIMARY KEY,ParentID int
-- Non-clustered index key can be up to 1600 bytes on SQL 2016+,FullPath varchar(1028) UNIQUE
)
;
INSERT @Hierarchy
( ID,FullPath )
VALUES
( 1,'(1)' ),( 2,'(1)/(2)' ),( 3,( 4,'(4)' ),( 5,'(4)/(5)' ),( 6,'(4)/(6)' ),( 7,'(4)/(6)/(7)' )
;
SELECT N.ID,A.ID,A.ParentID,A.FullPath
FROM @Hierarchy AS N
INNER JOIN @Hierarchy AS A ON N.FullPath LIKE A.FullPath + '%'
WHERE N.ID <> A.ID
ORDER BY N.ID ASC,A.ID ASC
;
在我的测试中,上面的查询确实在FullPath上使用了唯一索引。这是一种替代方法,但它执行完整扫描而不是使用索引:-
SELECT N.ID,A.FullPath
FROM @Hierarchy AS N
INNER JOIN @Hierarchy AS A ON SUBSTRING(N.FullPath,LEN(A.FullPath)) = A.FullPath
WHERE N.ID <> A.ID
ORDER BY N.ID ASC,A.ID ASC
;