将 CTE 与分层数据和 'cumulative' 值一起使用
Using CTE with hierarchical data and 'cumulative' values
我正在试验 SQL 常见 Table 表达式,使用城市、国家和大洲的示例层次结构以及哪些已访问过哪些尚未访问。
table t_hierarchy
看起来像这样:
(注意:non-cities 的 visited
列故意为 NULL,因为我希望这是一个动态计算的百分比。)
然后我使用以下 SQL 根据 t_hierarchy
中的数据创建了一个递归结果集:
WITH myCTE (ID, name, type, parentID, visited, Depth)
AS
(
Select ID, name, type, parentID, visited, 0 as Depth From t_hierarchy where parentID IS NULL
UNION ALL
Select t_hierarchy.ID, t_hierarchy.name, t_hierarchy.type, t_hierarchy.parentID, t_hierarchy.visited, Depth + 1
From t_hierarchy
inner join myCte on t_hierarchy.parentID = myCte.ID
)
Select ID, name, type, parentID, Depth, cnt.numDirectChildren, visited
FROM myCTE
LEFT JOIN (
SELECT theID = parentID, numDirectChildren = COUNT(*)
FROM myCTE
GROUP BY parentID
) cnt ON cnt.theID = myCTE.ID
order by ID
结果如下所示:
我现在想做的是创建一个专栏,例如visitedPercentage
以显示 访问过的城市的百分比 每个 'level' 层次结构(将城市与国家和大洲区别对待)。解释一下,我们一直在 'tree':
- 马德里将是 100%,因为它已被访问 (
visited
= 1)
- 巴塞罗那将是 0%,因为它没有被访问过(
visited
= 0)
- 西班牙因此是 50%,因为它有 2 个直接 children,一个是 100%,另一个是 0%
- 欧洲因此为 50%,因为西班牙为 50%,法国为 100%(巴黎已访问),德国为 0%(柏林 未 已访问)
我希望这是有道理的。我有点想说“如果它 不是 一个城市,根据所有直接 children 的 visitedPercentage
计算出这个级别的 visitedPercentage
, 否则只显示 100% 或 0%。非常感谢任何指导。
更新:
我已经使用 Daniel Gimenez 的建议将它进一步推进到我得到法国 100、西班牙 50 等的地步。但是顶级项目(例如欧洲)仍然是 0,如下所示:
我认为这是因为计算是在查询的递归部分之后而不是在其中完成的。 IE。这一行:
SELECT... , visitPercent = SUM(CAST visited AS int) / COUNT(*) FROM myCTE GROUP BY parentID
说的是 "look at the visited
column for child objects, calculate the SUM of the values, and show the result as visitPercent
",而它应该说的是 "look at the existing visitPercent
value from the previous calculation",如果这有意义的话。我不知道从这里去哪里! :)
我想我已经做到了,使用 2 个 CTE。最后,更容易获得每个级别(子孙等)的后代总数,并用它来计算总体百分比。
那很痛苦。有一次输入 'CATS' 而不是 'CAST' 让我困惑了大约 10 分钟。
with cte1 (ID,parentID,type,name,visited,Lvl) as (
select t.ID, t.parentID, t.type, t.name, t.visited, 0 as [Lvl]
from t_hierarchy t
where t.parentID is not null
union all
select c.ID, t.parentID, c.type, c.name, c.visited, c.Lvl + 1
from t_hierarchy t
inner join cte1 c on c.parentID = t.ID
where t.parentID is not null
),
cte2 (ID,name,type,parentID,parentName_for_reference,visited,Lvl) as (
Select t_hierarchy.ID, t_hierarchy.name, t_hierarchy.type, t_hierarchy.parentID, p.name as parentName_for_reference, t_hierarchy.visited, 0 as Lvl
From t_hierarchy
left join t_hierarchy p ON p.ID = t_hierarchy.parentID
where t_hierarchy.parentID IS NULL
UNION ALL
Select t_hierarchy.ID, t_hierarchy.name, t_hierarchy.type, t_hierarchy.parentID,p.name as parentName_for_reference, t_hierarchy.visited, Lvl + 1
From t_hierarchy
inner join cte2 on t_hierarchy.parentID = cte2.ID
inner join t_hierarchy p ON p.ID = t_hierarchy.parentID
)
select cte2.ID,cte2.name,cte2.type,cte2.parentID,cte2.parentName_for_reference,cte2.visited,cte2.Lvl
,CASE WHEN type = 'city' THEN 'N/A' ELSE CAST(cnt.totalDescendents as varchar) END AS totalDescendents
,CASE WHEN type = 'city' THEN 'N/A' ELSE CAST(COALESCE(cnt2.totalDescendentsVisited,0) as varchar) END AS totalDescendentsVisited
,CASE WHEN type = 'city' THEN 'N/A' ELSE CAST((CAST(ROUND(CAST(COALESCE(cnt2.totalDescendentsVisited,0) as float)/CAST(cnt.totalDescendents as float),2) AS numeric(36,2))*100) as varchar) END as asPercentage
from cte2
left JOIN (
SELECT theID = parentID, COUNT(*) as totalDescendents
FROM cte1
WHERE type = 'city'
GROUP BY parentID
) cnt ON cnt.theID = cte2.ID
left JOIN (
SELECT theID = parentID, COUNT(*) as totalDescendentsVisited
FROM cte1
WHERE type = 'city' AND visited = 1
GROUP BY parentID
) cnt2 ON cnt2.theID = cte2.ID
ORDER BY ID
这些帖子很有帮助:
Keeping it simple and how to do multiple CTE in a query
CTE to get all children (descendants) of a parent
我正在试验 SQL 常见 Table 表达式,使用城市、国家和大洲的示例层次结构以及哪些已访问过哪些尚未访问。
table t_hierarchy
看起来像这样:
(注意:non-cities 的 visited
列故意为 NULL,因为我希望这是一个动态计算的百分比。)
然后我使用以下 SQL 根据 t_hierarchy
中的数据创建了一个递归结果集:
WITH myCTE (ID, name, type, parentID, visited, Depth)
AS
(
Select ID, name, type, parentID, visited, 0 as Depth From t_hierarchy where parentID IS NULL
UNION ALL
Select t_hierarchy.ID, t_hierarchy.name, t_hierarchy.type, t_hierarchy.parentID, t_hierarchy.visited, Depth + 1
From t_hierarchy
inner join myCte on t_hierarchy.parentID = myCte.ID
)
Select ID, name, type, parentID, Depth, cnt.numDirectChildren, visited
FROM myCTE
LEFT JOIN (
SELECT theID = parentID, numDirectChildren = COUNT(*)
FROM myCTE
GROUP BY parentID
) cnt ON cnt.theID = myCTE.ID
order by ID
结果如下所示:
我现在想做的是创建一个专栏,例如visitedPercentage
以显示 访问过的城市的百分比 每个 'level' 层次结构(将城市与国家和大洲区别对待)。解释一下,我们一直在 'tree':
- 马德里将是 100%,因为它已被访问 (
visited
= 1) - 巴塞罗那将是 0%,因为它没有被访问过(
visited
= 0) - 西班牙因此是 50%,因为它有 2 个直接 children,一个是 100%,另一个是 0%
- 欧洲因此为 50%,因为西班牙为 50%,法国为 100%(巴黎已访问),德国为 0%(柏林 未 已访问)
我希望这是有道理的。我有点想说“如果它 不是 一个城市,根据所有直接 children 的 visitedPercentage
计算出这个级别的 visitedPercentage
, 否则只显示 100% 或 0%。非常感谢任何指导。
更新: 我已经使用 Daniel Gimenez 的建议将它进一步推进到我得到法国 100、西班牙 50 等的地步。但是顶级项目(例如欧洲)仍然是 0,如下所示:
我认为这是因为计算是在查询的递归部分之后而不是在其中完成的。 IE。这一行:
SELECT... , visitPercent = SUM(CAST visited AS int) / COUNT(*) FROM myCTE GROUP BY parentID
说的是 "look at the visited
column for child objects, calculate the SUM of the values, and show the result as visitPercent
",而它应该说的是 "look at the existing visitPercent
value from the previous calculation",如果这有意义的话。我不知道从这里去哪里! :)
我想我已经做到了,使用 2 个 CTE。最后,更容易获得每个级别(子孙等)的后代总数,并用它来计算总体百分比。
那很痛苦。有一次输入 'CATS' 而不是 'CAST' 让我困惑了大约 10 分钟。
with cte1 (ID,parentID,type,name,visited,Lvl) as (
select t.ID, t.parentID, t.type, t.name, t.visited, 0 as [Lvl]
from t_hierarchy t
where t.parentID is not null
union all
select c.ID, t.parentID, c.type, c.name, c.visited, c.Lvl + 1
from t_hierarchy t
inner join cte1 c on c.parentID = t.ID
where t.parentID is not null
),
cte2 (ID,name,type,parentID,parentName_for_reference,visited,Lvl) as (
Select t_hierarchy.ID, t_hierarchy.name, t_hierarchy.type, t_hierarchy.parentID, p.name as parentName_for_reference, t_hierarchy.visited, 0 as Lvl
From t_hierarchy
left join t_hierarchy p ON p.ID = t_hierarchy.parentID
where t_hierarchy.parentID IS NULL
UNION ALL
Select t_hierarchy.ID, t_hierarchy.name, t_hierarchy.type, t_hierarchy.parentID,p.name as parentName_for_reference, t_hierarchy.visited, Lvl + 1
From t_hierarchy
inner join cte2 on t_hierarchy.parentID = cte2.ID
inner join t_hierarchy p ON p.ID = t_hierarchy.parentID
)
select cte2.ID,cte2.name,cte2.type,cte2.parentID,cte2.parentName_for_reference,cte2.visited,cte2.Lvl
,CASE WHEN type = 'city' THEN 'N/A' ELSE CAST(cnt.totalDescendents as varchar) END AS totalDescendents
,CASE WHEN type = 'city' THEN 'N/A' ELSE CAST(COALESCE(cnt2.totalDescendentsVisited,0) as varchar) END AS totalDescendentsVisited
,CASE WHEN type = 'city' THEN 'N/A' ELSE CAST((CAST(ROUND(CAST(COALESCE(cnt2.totalDescendentsVisited,0) as float)/CAST(cnt.totalDescendents as float),2) AS numeric(36,2))*100) as varchar) END as asPercentage
from cte2
left JOIN (
SELECT theID = parentID, COUNT(*) as totalDescendents
FROM cte1
WHERE type = 'city'
GROUP BY parentID
) cnt ON cnt.theID = cte2.ID
left JOIN (
SELECT theID = parentID, COUNT(*) as totalDescendentsVisited
FROM cte1
WHERE type = 'city' AND visited = 1
GROUP BY parentID
) cnt2 ON cnt2.theID = cte2.ID
ORDER BY ID
这些帖子很有帮助:
Keeping it simple and how to do multiple CTE in a query
CTE to get all children (descendants) of a parent