我正在使用城市、国家和大陆的示例层次结构以及已访问过和未访问过的示例层次结构来试验 SQL 通用表表达式。
表格t_hierarchy
看起来像这样:
(注意:对于非城市,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
显示层次结构每个“级别”的访问过的城市的百分比(将城市与国家和大洲区别对待)。解释一下,沿着“树”往上走:
- 马德里为 100%,因为它已被访问过 (
visited
= 1) - 巴塞罗那将为 0%,因为它未被访问过 (
访问过
= 0) - 西类牙因此为 50%,因为它有 2 个直系子代,其中一个为 100%,另一个为 0%
- 因此,欧洲为 50%,因为西类牙为 50%,法国为 100%(已访问过巴黎),德国为 0%(未访问过柏林)
我希望这是有道理的。我想说“如果它不是一个城市,则根据所有直接子级的visitedPercentage
计算出该级别的visitedPercentage
,否则只显示 100% 或 0%。非常感谢任何指导。
更新: 我已经设法使用 Daniel Gimenez 进一步推进它我的建议是法国 100、西类牙 50 等。但顶级项目(例如欧洲)仍然为 0,如下所示:
我认为这是因为计算是在查询的递归部分之后完成的,而不是在其中完成的。 IE。这一行:
SELECT...、visitPercent = SUM(CAST 访问过 AS int)/COUNT(*) FROM myCTE GROUP BY ParentID
的意思是“查看子对象的 visited
列,计算值的总和,并将结果显示为 visitPercent
”,而它应该说“查看先前计算中的现有 visitPercent
值”,如果有意义的话。我不知道从这里该去哪里! :)
最佳答案
我想我已经做到了,使用 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
这些帖子很有帮助:
关于sql - 将 CTE 与分层数据和 'cumulative' 值结合使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34184052/