sql - 将 CTE 与分层数据和 'cumulative' 值结合使用

标签 sql sql-server-2008 common-table-expression hierarchical-data

我正在使用城市、国家和大陆的示例层次结构以及已访问过和未访问过的示例层次结构来试验 SQL 通用表表达式。

表格t_hierarchy看起来像这样:

The hierarchy table

(注意:对于非城市,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

结果如下所示:

cte result

我现在想做的,也是我正在努力做的,是创建一个专栏,例如visitedPercentage 显示层次结构每个“级别”的访问过的城市的百分比(将城市与国家和大洲区别对待)。解释一下,沿着“树”往上走:

  • 马德里为 100%,因为它已被访问过 (visited = 1)
  • 巴塞罗那将为 0%,因为它被访问过 (访问过 = 0)
  • 西类牙因此为 50%,因为它有 2 个直系子代,其中一个为 100%,另一个为 0%
  • 因此,欧洲为 50%,因为西类牙为 50%,法国为 100%(已访问过巴黎),德国为 0%(访问过柏林)

我希望这是有道理的。我想说“如果它不是一个城市,则根据所有直接子级的visitedPercentage计算出该级别的visitedPercentage,否则只显示 100% 或 0%。非常感谢任何指导。


更新: 我已经设法使用 Daniel Gimenez 进一步推进它我的建议是法国 100、西类牙 50 等。但顶级项目(例如欧洲)仍然为 0,如下所示:

enter image description here

我认为这是因为计算是在查询的递归部分之后完成的,而不是在其中完成的。 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

enter image description here

这些帖子很有帮助:

关于sql - 将 CTE 与分层数据和 'cumulative' 值结合使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34184052/

相关文章:

sql - 如何列出数据库链接 (Oracle) 中存在的所有表?

mysql - 从 INNER JOIN 3 表中获取值以及每个表中的最新记录

mysql - 更新条目而不更新时间戳

C# - 如果未提供字节数组,如何传递空值?

sql - 如何在不使用子查询的情况下仅选择具有最大序列的行?

sql-server - 在 SQL Server 中通过递归 CTE 创建排列?

php - SQL/PHP : get all results within time X to Y, 检测中间是否有可用时间

sql - SQL Server 数据库中具有最大大小的 INT 类型

SQL 查询使用列作为公式来计算值

SQL 查询汇总年初至今的实际值和 YTD 的平均值