自连接时展平层次结构 table

Flatten hierarchy on self-join table

我在自连接层次结构 table 中有数据,其中 Continents 有很多 Countries 有很多 地区有很多有很多城市

自连接table结构:

|-------------------------------------------------------------|
| ID  | Name          |  Type       |  ParentID  | IsTopLevel |
|-------------------------------------------------------------|
| 1   | North America |  Continent  |  NULL      | 1          |
| 12  | United States |  Country    |  1         | 0          |
| 113 | Midwest       |  Region     |  12        | 0          |
| 155 | Kansas        |  State      |  113       | 0          |
| 225 | Topeka        |  City       |  155       | 0          |
| 2   | South America |  Continent  |  NULL      | 1          |
| 22  | Argentina     |  Country    |  2         | 0          |
| 223 | Southern      |  Region     |  22        | 0          |
| 255 | La Pampa      |  State      |  223       | 0          |
| 777 | Santa Rosa    |  City       |  255       | 0          |
|-------------------------------------------------------------|

我已经能够成功地使用递归CTE 来获取每个节点的树结构和深度。我失败的地方是使用枢轴创建一个很好的列表,其中包含所有底部位置及其在每个级别的相应父级。

预期结果:

|------------------------------------------------------------------------------------|
| Continent     | Country       | Region   | State    | City       | Bottom_Level_ID |
|------------------------------------------------------------------------------------|
| North America | United States | Midwest  | Kansas   | Topeka     | 234             |
| South America | Argentina     | Southern | La Pampa | Santa Rosa | 777             |
|------------------------------------------------------------------------------------|

有几个要点我需要澄清一下。

  1. 每个条目都有一个底层和一个顶层。没有 给定位置不存在所有五种类型的情况。

  2. 如果我填写此数据,我将有 50 个北美条目 州级,所以你可以想象这个 table 在 地球上每个大陆的城市级别。 十亿 行。

  3. 之所以这是必需的,是因为我需要能够加入一个人曾居住过的所有地址的历史 table,并沿着树向上移动。我想如果我有来自那个 table 的 LocationID,我就可以 LEFT JOIN 到这个查询的视图上并获取适当的列。

  4. 这是一个旧数据库,2005 年,我没有系统管理员或架构控制权。

我的 CTE 代码

--CTE
;WITH Tree
AS (
  SELECT ID, Name, ParentID, Type, 1 as Depth
  FROM LocationTable
  WHERE IsTopLevel = 1
  UNION ALL
  SELECT L.ID, L.Name, L.ParentID, L.Type, T.Depth+1
  FROM Tree T
    JOIN LocationTable L
      ON L.ParentGUID = T.GUID
)

良好的可靠数据,采用最有用的格式。但是后来我开始考虑它并且 table 结构不是已经采用这种格式,那么如果我不打算同时将条目连接在一起,为什么还要费心进行深度树搜索呢?

总之,剩下的就到这里了。

枢轴尝试

;WITH Tree
AS (
  SELECT ID, Name, ParentID, Type
  FROM LocationTable
  WHERE IsTopLevel = 1
  UNION ALL
  SELECT L.ID, L.Name, L.ParentID, L.Type
  FROM Tree T
    JOIN LocationTable L
      ON L.ParentGUID = T.GUID
)
select *
from Tree
pivot (
    max(Name)
    for Type in ([Continent],[Country],[Region],[State],[City])
) pvt

现在我在一列中按类型列出了所有内容,其他所有内容均为空值。正如我之前一直在努力解决的那样,我需要 filter/join CTE 数据才能尝试我的支点,但我不知道从哪里开始。我尝试过的一切都是 soooooooooo sloooooooow。

每当我认为我了解 CTE 和 Pivot 时,一些新的东西让我感到非常谦卑。请帮我。 ; ;

如果您的结构像您描述的那样干净(没有间隙,总是 5 个级别),您可能会选择简单的方法:

这个数据确实需要一个经典的 1:n-table-tree,你的国家、州等生活在他们自己的 table s 和 link 到他们的父记录

确保ParentID和ID有索引!

DECLARE @tbl TABLE(ID INT,Name VARCHAR(100),Type VARCHAR(100),ParentID INT,IsTopLevel BIT);
INSERT INTO @tbl VALUES
 (1,'North America','Continent',NULL,1)
,(12,'United States','Country',1,0)
,(113,'Midwest','Region',12,0)
,(155,'Kansas','State',113,0)
,(225,'Topeka','City',155,0)
,(2,'South America','Continent',NULL,1)
,(22,'Argentina','Country',2,0)
,(223,'Southern','Region',22,0)
,(255,'La Pampa','State',223,0)
,(777,'Santa Rosa','City',255,0);

SELECT Level1.Name AS Continent
      ,Level2.Name AS Country
      ,Level3.Name AS Region
      ,Level4.Name AS State
      ,Level5.Name AS City
      ,Level5.ID AS Bottom_Level_ID
FROM @tbl AS Level1 
    INNER JOIN @tbl AS Level2 ON Level1.ID=Level2.ParentID
        INNER JOIN @tbl AS Level3 ON Level2.ID=Level3.ParentID
            INNER JOIN @tbl AS Level4 ON Level3.ID=Level4.ParentID
                INNER JOIN @tbl AS Level5 ON Level4.ID=Level5.ParentID
WHERE Level1.ParentID IS NULL

结果

Continent       Country         Region      State       City      Bottom_Level_ID
North America   United States   Midwest     Kansas      Topeka      225
South America   Argentina       Southern    La Pampa    Santa Rosa  777

CTE 的另一个解决方案可能是:

;WITH Tree
AS (
  SELECT cast(NULL as varchar(100)) as C1, cast(NULL as varchar(100)) as C2, cast(NULL as varchar(100)) as C3, cast(NULL as varchar(100)) as  C4, Name as C5, ID as B_Level
  FROM LocationTable
  WHERE IsTopLevel = 1
  UNION ALL
  SELECT T.C2, T.C3, T.C4, T.C5, L.Name, L.ID
  FROM Tree T
    JOIN LocationTable L
      ON L.ParentID = T.B_Level
)
select *
from Tree
where C1 is not null