Fast way to “flatten” hierarchy table?

允我心安 提交于 2019-12-23 17:57:47

问题


I've got a very huge table with hierarchy which can not be modified. Nodes in the table have an Id, ParentId, a Level and some data. The Level means that node with level N can be a child not only for level N-1 but also for level N-2, N-3 etc. The good news are that the number of levels is limited - there are only 8 of them. Level 1 is on the top of the hierarchy and level 8 is the end of it.

And now I need to flatten that table with respect to the place of the levels. The result should be like this:

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

First step As the level number is limited, the first idea was to LEFT JOIN several times all the table on ParentId = Id. But this made levels change their place, as level 6 could be skipped and level 5 took it's place.

Second step So I've used CASE WHEN to select value depending on the row's level.

-- 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,

It solved my problem but it was VERY slow.

Third step The combination of levels is also limited (1-2-3-4-5-6-7-8, 1-3-5-6-7-8, etc.) So I decided to use more LEFT JOINs to glue all combinations of levels together:

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

And then I selected data using COALESCE:

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

It made my query VERY complicated and hard to extend, but as for now it's the fastest result I've achieved.

Are there any faster and tiny ways to flattern that table? Any help will be appreciated.

Thanks in advance!


回答1:


This is an example how you could go with a recursive CTE:

To be honest: I'd not expect this to be very fast with huge data...

There is the HIERARCHYID data type, but you said, that you are not allowed to change the table's structure...

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

The result

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


来源:https://stackoverflow.com/questions/35550757/fast-way-to-flatten-hierarchy-table

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!