递归连接
Recursive joins
我在下面定义了两个 table(请注意 Regions table 是递归的,并且递归可能有很多级别)。
地区
Id
ParentId
Name
1
null
EU
2
1
Germany
3
1
France
城市
Id
Name
RegionId
1
Berlin
2
2
Hamburg
2
3
Paris
3
4
Nice
3
我想看看某个地区有多少个城市。所需输出如下:
Region
CityCount
EU
4
Germany
2
France
2
此查询为我提供了每个子区域中的城市数量,但我如何加入递归 table 以获得父区域(在本例中为 EU
)区域?
select R.Name, count(C.Id)
from Regions R
join Cities C on C.RegionId = R.Id
group by R.Name
having count(C.Id) > 1
我试图简化我面临的现实问题,这显然是简化。
您可以使用递归 CTE 来展平您的 regions
树:
with flatregions as
(
select t.ID, t.ParentID, t.Name, 1 as lvl
from regions t
union all
select t.ID, tt.ParentID, tt.Name, t.lvl+1
from flatregions t
inner join regions tt on tt.ID = t.ParentID
)
select * FROM flatregions;
ID
ParentID
Name
lvl
1
EU
1
2
1
Germany
1
3
1
France
1
3
EU
2
2
EU
2
然后在 cities
table 的 JOIN
中使用该 CTE:
with flatregions as
(
select t.ID, t.ParentID, t.Name, 1 as lvl
from regions t
union all
select t.ID, tt.ParentID, tt.Name, t.lvl+1
from flatregions t
inner join regions tt on tt.ID = t.ParentID
)
select R.Name, count(C.Id) as CityCount
from flatregions R
join Cities C on C.RegionId = R.Id
group by R.Name
having count(C.Id) > 1
Region
CityCount
EU
4
Germany
2
France
2
参见 this db<>fiddle。
这似乎是您想要的。您可以使用 rCTE 通过层次结构移动到根,但每次迭代都会保留某些信息;在这种情况下,原始节点的名称。然后你仍然可以 JOIN
在 RegionID
:
WITH rCTE AS(
SELECT R.ID,
R.ParentID,
R.[Name]
FROM dbo.Regions R
UNION ALL
SELECT R.ID,
R.ParentID,
C.[Name]
FROM dbo.Regions R
JOIN rCTE C ON R.ParentID = C.ID)
SELECT r.[Name],
COUNT(*) AS CityCount
FROM rCTE r
JOIN dbo.Cities C ON r.ID = C.RegionID
GROUP BY r.[Name];
我在下面定义了两个 table(请注意 Regions table 是递归的,并且递归可能有很多级别)。
地区
Id | ParentId | Name |
---|---|---|
1 | null | EU |
2 | 1 | Germany |
3 | 1 | France |
城市
Id | Name | RegionId |
---|---|---|
1 | Berlin | 2 |
2 | Hamburg | 2 |
3 | Paris | 3 |
4 | Nice | 3 |
我想看看某个地区有多少个城市。所需输出如下:
Region | CityCount |
---|---|
EU | 4 |
Germany | 2 |
France | 2 |
此查询为我提供了每个子区域中的城市数量,但我如何加入递归 table 以获得父区域(在本例中为 EU
)区域?
select R.Name, count(C.Id)
from Regions R
join Cities C on C.RegionId = R.Id
group by R.Name
having count(C.Id) > 1
我试图简化我面临的现实问题,这显然是简化。
您可以使用递归 CTE 来展平您的 regions
树:
with flatregions as
(
select t.ID, t.ParentID, t.Name, 1 as lvl
from regions t
union all
select t.ID, tt.ParentID, tt.Name, t.lvl+1
from flatregions t
inner join regions tt on tt.ID = t.ParentID
)
select * FROM flatregions;
ID | ParentID | Name | lvl |
---|---|---|---|
1 | EU | 1 | |
2 | 1 | Germany | 1 |
3 | 1 | France | 1 |
3 | EU | 2 | |
2 | EU | 2 |
然后在 cities
table 的 JOIN
中使用该 CTE:
with flatregions as
(
select t.ID, t.ParentID, t.Name, 1 as lvl
from regions t
union all
select t.ID, tt.ParentID, tt.Name, t.lvl+1
from flatregions t
inner join regions tt on tt.ID = t.ParentID
)
select R.Name, count(C.Id) as CityCount
from flatregions R
join Cities C on C.RegionId = R.Id
group by R.Name
having count(C.Id) > 1
Region | CityCount |
---|---|
EU | 4 |
Germany | 2 |
France | 2 |
参见 this db<>fiddle。
这似乎是您想要的。您可以使用 rCTE 通过层次结构移动到根,但每次迭代都会保留某些信息;在这种情况下,原始节点的名称。然后你仍然可以 JOIN
在 RegionID
:
WITH rCTE AS(
SELECT R.ID,
R.ParentID,
R.[Name]
FROM dbo.Regions R
UNION ALL
SELECT R.ID,
R.ParentID,
C.[Name]
FROM dbo.Regions R
JOIN rCTE C ON R.ParentID = C.ID)
SELECT r.[Name],
COUNT(*) AS CityCount
FROM rCTE r
JOIN dbo.Cities C ON r.ID = C.RegionID
GROUP BY r.[Name];