【问题标题】:Fast way to "flatten" hierarchy table?“扁平化”层次结构表的快速方法?
【发布时间】:2016-06-03 17:01:09
【问题描述】:

我有一个非常大的表,其层次结构无法修改。表中的节点有IdParentIdLevel 和一些数据。 Level 表示级别为N 的节点不仅可以是级别N-1 的子节点,还可以是级别N-2N-3 等的子节点。好消息是级别的数量是有限的——有只有8个。级别 1 位于层次结构的顶部,级别 8 位于其末尾。

现在我需要根据关卡的位置将该表展平。结果应该是这样的:

Lvl1   Lvl2   Lvl3   ...   Lvl8
xxx    xxx    null         xxx
xxx    null   xxx          xxx
xxx    null   null         xxx
xxx    xxx    xxx          xxx

第一步 由于级别数有限,第一个想法是LEFT JOIN 数倍于ParentId = Id 上的所有表。但这使得关卡的位置发生了变化,因为可以跳过第 6 关,而用第 5 关代替。

第二步所以我使用CASE WHEN根据行的级别选择值。

-- LEVEL 4
CASE
    WHEN lvl6.[Level] = 4 THEN lvl6.Data -- in case levels 6 and 5 were skipped, we can find 4th level data here
    WHEN lvl5.[Level] = 4 THEN lvl5.Data
    WHEN lvl4.[Level] = 4 THEN lvl4.Data
    ELSE NULL
END AS l4Data,

它解决了我的问题,但速度很慢。

第三步 关卡的组合也是有限的(1-2-3-4-5-6-7-81-3-5-6-7-8 等)所以我决定使用更多的LEFT JOINs 将所有关卡组合粘合在一起:

WITH
   l7 AS (SELECT * FROM myTable WHERE [Level] = 7),
   l6 AS (SELECT * FROM myTable WHERE [Level] = 6),
...
FROM l7
...
LEFT JOIN l6 AS l6_7 ON l7.ParentId = l6_7.Id       -- 7-6-5-4-1
LEFT JOIN l5 AS l5_7 ON l6_7.ParentId = l5_7.Id
LEFT JOIN l4 AS l4_7 ON l5_7.ParentId = l4_7.Id
LEFT JOIN l1 AS l1_7 ON l4_7.ParentId = l1_7.Id

然后我使用COALESCE选择数据:

COALESCE(l3.Data, l3_1.Data, l3_2.Data, l3_3.Data) AS l3Data,

这使我的查询非常复杂且难以扩展,但就目前而言,这是我取得的最快结果。

有没有更快更小的方法来平整这张桌子?任何帮助将不胜感激。

提前致谢!

【问题讨论】:

  • 典型用途是CTE,什么版本的SQL Server?
  • 谢谢,如果下面的答案不清楚,请回复评论,我会发布一些东西
  • 非常感谢您的帮助,因为我看不到 Shnugo 的答案如何处理跳过一个或多个级别的情况
  • 一般来说,你可以有简洁的代码或快速的代码,但通常不能两者兼得
  • 您可能需要在问题中澄清(请提供示例)是第 7 层上的给定节点 A 可以在第 2 层上具有直接父节点 B,并且没有定义中间层。如果您将其作为示例数据提供,每个人可能会更清楚(并确认此假设)

标签: sql-server tsql sql-server-2012 hierarchy bigdata


【解决方案1】:

这是一个如何使用递归 CTE 的示例:

说实话:对于大量数据,我不认为这会非常快...

HIERARCHYID data type,但是你说不能改变表的结构...

DECLARE @t TABLE(Name VARCHAR(100),id INT,parentId INT);

INSERT INTO @t VALUES
('Element 1',1,0)
,('Element 1.1',2,1)
,('Element 1.2',3,1)
,('Element 1.3',4,1)

,('Element 1.1.1',5,2)
,('Element 1.1.2',6,2)
,('Element 1.2.1',7,3)

,('Element 1.2.1.1',8,7)
,('Element 1.2.1.2',9,7);


WITH CTE AS
( 
       SELECT   * 
              ,CAST(parentId AS VARCHAR(MAX))  + ',' + CAST(CAST(id AS VARCHAR(MAX)) AS VARCHAR(MAX)) AS IdListTopDown
                ,CAST(Name AS varchar(MAX)) AS NameList
    FROM @t
    WHERE parentId = 0
    UNION ALL
    SELECT t.* 
             ,CAST(c.IdListTopDown AS VARCHAR(MAX)) + ',' + CAST(CAST(t.id AS VARCHAR(MAX)) AS VARCHAR(MAX))
               ,CAST(c.NameList + ' | ' + t.Name AS varchar(MAX))
       FROM @t AS t
       JOIN CTE c ON c.id = t.parentId
)
SELECT  CTE.*
FROM  CTE
WHERE NOT EXISTS(SELECT * FROM @t WHERE parentId=CTE.id)
ORDER BY CTE.IdListTopDown

结果

Element 1.1.1       5   2   0,1,2,5     Element 1 | Element 1.1 | Element 1.1.1
Element 1.1.2       6   2   0,1,2,6     Element 1 | Element 1.1 | Element 1.1.2
Element 1.2.1.1     8   7   0,1,3,7,8   Element 1 | Element 1.2 | Element 1.2.1 | Element 1.2.1.1
Element 1.2.1.2     9   7   0,1,3,7,9   Element 1 | Element 1.2 | Element 1.2.1 | Element 1.2.1.2
Element 1.3         4   1   0,1,4       Element 1 | Element 1.3

【讨论】:

  • 我以您的代码为起点。并且在我的表上执行需要几分钟(大约 1.2KK 记录)
  • @Waldemar,恐怕没有真正快速的方法......尤其是像间隙这样的额外规则。在类似的情况下,我有一堆CROSS APPLYs 的查询。我从最外层的元素(那些,其 ID 不用作 parentID 的元素)开始并向上处理层次结构。优点:您可以肯定,有一个父母(因此 CROSS )但这也不是很快。就我而言,我使用每晚的工作来写一张平板......
  • 感谢您的帮助。现在,我在步骤 3 中描述的解决方案需要 3-5 秒来搜索视图,我开始考虑将该视图存储在表中以提高搜索速度。
  • 关于:层次结构。虽然 OP 可能无法更改表的结构,但他/她可以创建一个新表来引用这个只有 ID 和计算的 hierarchyid 的表。假设它在 DML 操作上保持最新,您可以在无需修改基表的情况下获得 hierarchyid 的速度。
  • @BenThul 好的,但在这种情况下,我更喜欢带有扁平数据的触发边表(至少 ID 并排......)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-03-14
  • 1970-01-01
  • 2021-01-01
  • 2022-01-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多