我在 Microsoft SQL Server 中有一个表,其中包含一些列,它还包含 2 列用于分层数据。层次结构可以非常深,例如超过 15 级,并且表中可以有大量行。表结构是像下面这样:
表名称:tbl1
col_a(vachar) col_b(varchar) col_c(varchar) col_d(varchar) ....more columns
----------------------------------------------------------------------------------
12 16 Japan 3
16 17 Australia 2
17 0 Panama 1
19 12 CostaRica 4
22 16 Brazil 3
11 12 Bhutan 4
14 17 Japan 2
....more rows
如果为此构建一棵树,它将如下所示:
17 (Panama)
|
|__16 (Australia)
| |
| |__12 (Japan)
| | |
| | |__ 19 (CostaRica)
| | |
| | |__11 (Bhutan)
| |
| |__22 (Brazil)
|
|__14 (Japan)
现在如果我搜索日本,我应该会看到以下内容
17 (Panama)
|
|__16 (Australia)
| |
| |__ 12(Japan)
|
|__14(Japan)
col_a
包含每行的 id 值,col_b
包含每行的父 id 值。
例如。上面的行中 17 是第一个节点(预定义为第一个节点的 id 为 17,我不需要检查 17 是否有父节点),16 和 14 是 17 的子节点。然后 12 和22 是 16 的子节点,依此类推。
col_c
有一些文本(此处给出国家/地区名称是出于例如目的)。 col_d
有级别值。
我需要以树格式显示分层数据。还必须在树上实现搜索。
我尝试了两种显示树结构的方法。我可以显示树,但无法实现搜索。
现在我首先尝试从存储过程中获取 XML 格式的数据,然后将该 XML 转换为 JSON(我的 SQL Server 版本不支持
FOR JSON PATH
,但它支持 FOR XML PATH
)。因此,我尝试获取整个树 JSON 并使用 Angular 的 Ng Prime 树组件来显示/搜索树。
为此,我在 SQL Server 中尝试了以下方法:
WITH cte1 AS
(
SELECT col_a, col_b, col_c, col_d
FROM tbl1
WHERE col_a = '17'
), cte2 AS
(
SELECT col_a, col_b, col_c, col_d
FROM tbl1 AS t
WHERE t.col_b IN (SELCET col_a FROM cte1)
),
cte3 AS
(
SELECT col_a, col_b, col_c, col_d
FROM tbl1 AS t
WHERE t.col_b IN (SELECT col_a FROM cte2)
)
SELECT
cte1.col_a, cte1.col_b, cte1.col_c, cte1.col_d,
cte2.col_a, cte2.col_b, cte2.col_c, cte2.col_d,
cte3.col_a, cte3.col_b, cte3.col_c, cte3.col_d
FROM
cte1
LEFT JOIN
cte2 ON cte2.col_b = cte1.col_a
LEFT JOIN
cte3 ON cte3.colb = cte2.col_a;
由于我们不知道层次结构可以有多深,所以我从表中获取了最大级别并将上述查询构建为字符串,然后使用
sp_executesql
运行它。这种方法是有效的,但当数据大小和层次结构深度非常大时,它就会失败。
我也尝试过递归 CTE,但由于相同的大量行和大深度,这也不起作用。
因此,由于无法一次获取整个树数据,我尝试了不同的方法。
在第二种方法中,我没有使用任何库中的任何组件,并且我以角度构建了一个简单的组件,它将显示分层列表。在此列表中,我首先仅显示节点 17,当用户单击节点 17 时,我调用 API 控制器,该控制器又调用存储过程来获取父级为 17 的所有节点,即节点 16 和 14。
然后,当用户单击节点 16 时,将返回并显示父节点为 16 的节点,即 12 和 22。这工作正常,我已经使用这种方法做了一些工作。但现在的问题是搜索。我不知道如何以上述格式显示搜索结果。当用户搜索文本(例如日本)时,我应该如何获取并显示数据(请参阅上面给出的搜索)?
嗯,对于 CTE,我会选择这样的东西:
;with data as (
select *
from (
VALUES(12, 16 ,N'Japan' ,3)
,(16, 17 ,N'Australia' ,2)
,(17, 0 ,N'Panama' ,1)
,(19, 12 ,N'CostaRica' ,4)
,(22, 16 ,N'Brazil' ,3)
,(11, 12 ,N'Bhutan' ,4)
,(14, 17 ,N'Japan' ,2)
) a(id, parent, name,x)
)
, hierarchy as (
SELECT id, CAST(id AS NVARCHAR(MAX)) + '_' AS path, name, name AS name2, 0 level, parent
FROM data d
where parent > 0
UNION ALL
SELECT d.id, h.path + CAST(d.id AS NVARCHAR(MAX)) + '_' AS path, h.name, d.name, h.level + 1, d.parent
FROM data d
INNER JOIN hierarchy h
ON h.parent = d.id
)
select REPLICATE('|_', level) + name2, path, id, parent,name, name2
from hierarchy
--where name = 'japan'
order by path
;with data as (
select *
from (
VALUES(12, 16 ,N'Japan' ,3)
,(16, 17 ,N'Australia' ,2)
,(17, 0 ,N'Panama' ,1)
,(19, 12 ,N'CostaRica' ,4)
,(22, 16 ,N'Brazil' ,3)
,(11, 12 ,N'Bhutan' ,4)
,(14, 17 ,N'Japan' ,2)
) a(id, parent, name,x)
)
, hierarchy as (
SELECT id, CAST(id AS NVARCHAR(MAX)) + '_' AS path, name, 0 level
FROM data
WHERE parent = 0
UNION ALL
SELECT d.id, h.path + CAST(d.id AS NVARCHAR(MAX)) + '_' AS path, d.name, h.level + 1
FROM data d
INNER JOIN hierarchy h
ON h.id = d.parent
)
select REPLICATE('|_', level) + name, path, id
from hierarchy
order by path
第一个是数据的“以元素为中心”视图,即。它跟踪每个元素的父元素,它在搜索或类似的事情中可能很有用。
第二个是定期查看数据。如果正确索引“parent”和“id”,则获取不会太慢。但话虽如此,加载它后,您应该将其保留在内存中而不是访问数据库。
路径可以用作标识符和树构建块
首先,MS SQL Server 中有一个专用的数据类型来执行此操作。 HierarchyId 与许多操作树结构的方法相关联...
但是我处理树的首选方法是将经典的邻接表转换为嵌套区间......
了解此方法:嵌套集合模型