拼合邻接表层次到一个列表中所有路径的
-
11-09-2019 - |
题
我具有存储使用邻接表模型层次的信息的表。 (使用自参照键 - 例如低于此表可能看起来熟悉一>):
category_id name parent
----------- -------------------- -----------
1 ELECTRONICS NULL
2 TELEVISIONS 1
3 TUBE 2
4 LCD 2
5 PLASMA 2
6 PORTABLE ELECTRONICS 1
7 MP3 PLAYERS 6
8 FLASH 7
9 CD PLAYERS 6
10 2 WAY RADIOS 6
<强>什么是“平坦”的上述数据代入像这样的最佳方法吗
category_id lvl1 lvl2 lvl3 lvl4
----------- ----------- ----------- ----------- -----------
1 1 NULL NULL NULL
2 1 2 NULL NULL
6 1 6 NULL NULL
3 1 2 3 NULL
4 1 2 4 NULL
5 1 2 5 NULL
7 1 6 7 NULL
9 1 6 9 NULL
10 1 6 10 NULL
8 1 6 7 8
每个行是一个“路径”通过层次结构,除了有一排每个节点(不只是各叶节点)。所述CATEGORY_ID列表示当前节点和“拉特”列是它的祖先。当前节点的值也必须在最右端拉特列。在LVL1列将始终代表根节点的值,在LVL2值将始终代表LVL1直接后代,等等。
如果能够产生此输出将是SQL,和将工作的n层层次结构。所述方法
解决方案
要跨越做一个简单的邻接表多层次的查询总是涉及自左联接。这很容易使一个右对齐的表:
SELECT category.category_id,
ancestor4.category_id AS lvl4,
ancestor3.category_id AS lvl3,
ancestor2.category_id AS lvl2,
ancestor1.category_id AS lvl1
FROM categories AS category
LEFT JOIN categories AS ancestor1 ON ancestor1.category_id=category.category_id
LEFT JOIN categories AS ancestor2 ON ancestor2.category_id=ancestor1.parent
LEFT JOIN categories AS ancestor3 ON ancestor3.category_id=ancestor2.parent
LEFT JOIN categories AS ancestor4 ON ancestor4.category_id=ancestor3.parent;
要左对齐它喜欢你的例子是比较麻烦一些。此想到:
SELECT category.category_id,
ancestor1.category_id AS lvl1,
ancestor2.category_id AS lvl2,
ancestor3.category_id AS lvl3,
ancestor4.category_id AS lvl4
FROM categories AS category
LEFT JOIN categories AS ancestor1 ON ancestor1.parent IS NULL
LEFT JOIN categories AS ancestor2 ON ancestor1.category_id<>category.category_id AND ancestor2.parent=ancestor1.category_id
LEFT JOIN categories AS ancestor3 ON ancestor2.category_id<>category.category_id AND ancestor3.parent=ancestor2.category_id
LEFT JOIN categories AS ancestor4 ON ancestor3.category_id<>category.category_id AND ancestor4.parent=ancestor3.category_id
WHERE
ancestor1.category_id=category.category_id OR
ancestor2.category_id=category.category_id OR
ancestor3.category_id=category.category_id OR
ancestor4.category_id=category.category_id;
将工作的n层层次结构。
对不起,任意深度的查询是不可能在邻接列表模型。如果你正在做这种查询了很多,你应该将模式切换到的其他型号的:全邻接关系(存储所有祖先的后代关系),物化路径,或嵌套集合
如果类别不走动很多(通常是像您的例子的店铺的情况下),我会趋向嵌套集。
其他提示
如所提到的,SQL没有干净的方式来实现与列的动态变化数量的表。我以前使用的只有两种解决方法: 1.一种固定数量的自联接,给列的固定数量(AS每BobInce) 2.生成的结果作为字符串在单个列
最初的第二个声音奇形怪状;存储ID作为字符串?但是,当输出格式为XML或什么的,人们似乎并不介意这么多。
同样,这是很少使用的,如果你那么想加入对SQL的结果。如果结果是要提供给应用程序,它可以是非常合适的。就我个人而言,我喜欢做的应用程序,而不是SQL扁平化
,点击 我在这里停留一个10英寸的屏幕上没有访问SQL,所以我不能给被测试代码,但基本的方法是利用某种方式递归;点击 - 递归标量函数可以做到这一点点击 - MS SQL可使用WITH语句递归做到这一点(更有效)
<强>标量函数(像):强>
CREATE FUNCTION getGraphWalk(@child_id INT)
RETURNS VARCHAR(4000)
AS
BEGIN
DECLARE @graph VARCHAR(4000)
-- This step assumes each child only has one parent
SELECT
@graph = dbo.getGraphWalk(parent_id)
FROM
mapping_table
WHERE
category_id = @child_id
AND parent_id IS NOT NULL
IF (@graph IS NULL)
SET @graph = CAST(@child_id AS VARCHAR(16))
ELSE
SET @graph = @graph + ',' + CAST(@child_id AS VARCHAR(16))
RETURN @graph
END
SELECT
category_id AS [category_id],
dbo.getGraphWalk(category_id) AS [graph_path]
FROM
mapping_table
ORDER BY
category_id
我有一段时间没有使用的是递归的,但我给的语法去,即使我没有SQL在这里做测试任何东西:)
<强>递归WITH 强>
WITH
result (
category_id,
graph_path
)
AS
(
SELECT
category_id,
CAST(category_id AS VARCHAR(4000))
FROM
mapping_table
WHERE
parent_id IS NULL
UNION ALL
SELECT
mapping_table.category_id,
CAST(result.graph_path + ',' + CAST(mapping_table.category_id AS VARCHAR(16)) AS VARCHAR(4000))
FROM
result
INNER JOIN
mapping_table
ON result.category_id = mapping_table.parent_id
)
SELECT
*
FROM
result
ORDER BY
category_id
,点击 修改 - 输出两者是相同的:强>
1 '1'
2 '1,2'
3 '1,2,3'
4 '1,2,4'
5 '1,2,5'
6 '1,6'
7 '1,6,7'
8 '1,6,7,8'
9 '1,6,9'
遍历任意深度的树通常涉及递归程序代码,除非你使用一些DBMS的特殊功能。
在Oracle中,CONNECT BY子句将允许您遍历树深度优先顺序,如果你使用邻接表,因为你没有在这里。
如果您使用嵌套组,左序列号会为您提供的顺序访问节点。
实际上可以与存储程序内动态SQL来完成。然后,您成为限于可以西斯的存储过程来完成。显然,这成为给EXEC结果到一个临时表,不知道有多少列期待一个挑战。但是,如果我们的目标是要输出到网页或其他UI那么它可能是值得的......