自连接时展平层次结构 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 |
|------------------------------------------------------------------------------------|
有几个要点我需要澄清一下。
每个条目都有一个底层和一个顶层。没有
给定位置不存在所有五种类型的情况。
如果我填写此数据,我将有 50 个北美条目
州级,所以你可以想象这个 table 在
地球上每个大陆的城市级别。 十亿 行。
之所以这是必需的,是因为我需要能够加入一个人曾居住过的所有地址的历史 table,并沿着树向上移动。我想如果我有来自那个 table 的 LocationID,我就可以 LEFT JOIN 到这个查询的视图上并获取适当的列。
这是一个旧数据库,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
我在自连接层次结构 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 |
|------------------------------------------------------------------------------------|
有几个要点我需要澄清一下。
每个条目都有一个底层和一个顶层。没有 给定位置不存在所有五种类型的情况。
如果我填写此数据,我将有 50 个北美条目 州级,所以你可以想象这个 table 在 地球上每个大陆的城市级别。 十亿 行。
之所以这是必需的,是因为我需要能够加入一个人曾居住过的所有地址的历史 table,并沿着树向上移动。我想如果我有来自那个 table 的 LocationID,我就可以 LEFT JOIN 到这个查询的视图上并获取适当的列。
这是一个旧数据库,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