【问题标题】:Recursive Query and Count (SQL Server)递归查询和计数 (SQL Server)
【发布时间】:2011-07-19 21:00:37
【问题描述】:

我正在尝试使用这四个简化表编写查询:

组织
(pk) 组织 ID
组织名称
(fk) ParentOrganizationID

人员
(pk) 人员 ID
(fk) 组织 ID
名称

活动
(pk) 事件ID
事件名称

事件日志
(pk) 人员 ID
(pk) 事件ID
参与时间

我想创建一个查询,该查询将 eventID 和 organizationID 作为参数并返回一个表,该表返回所有组织的名称、组织和子组织中的总数以及事件中的参与者总数对于组织和它的孩子。一个示例返回可能是:

OrganizationName   | TotalNumberInOrganization | TotalParticipatingInEvent  
TopOrganization    |         200               |            150      
 SecondTier1       |         150               |            100 
  Tier1Child       |          50               |             50
  Tier1Child2      |          50               |             25
 SecondTier2       |          25               |             25

顶级组织是其所有子组织 SecondTier1 和 SecondTier2 及其自身的总和。 SecondTier1 是它的所有孩子 Teir1Child 和 Tier1Child2 以及它自己的总和。这将继续计算所有子项和总数。

我知道如何使用递归 CTE 仅返回一个条目,例如仅返回顶级组织的总数,但我不确定如何获取所有组织及其子级的总数。任何帮助将不胜感激。

这里要求的是我用来返回有关组织的单行的过程。

由于某种原因,如果我在 UNION 中添加“U”,它会引发网络错误并且不允许我对其进行编辑。

@OrganizationID uniqueidentifier
@EventID uniqueidentifier

WITH OrganizationList(OrganizationID) AS
    (SELECT Organization.OrganizationID
    FROM Organization
    WHERE OrganizationID = @OrganizationID
    NION ALL
    SELECT Organization.OrganizationID
    FROM Organization 
    INNER JOIN OrganizationList ON Organization.ParentOrganizationID = OrganizationList.OrganizationID)

SELECT OrganizationAbbreviation,

       (SELECT COUNT(*)
        FROM Personnel
        WHERE Personnel.OrganizationID IN (SELECT OrganizationID FROM OrganizationList))
        AS OrganizationTotal,

        (SELECT COUNT(*)
         FROM Personnel 
         INNER JOIN EventLog ON EventLog.PersonnelID = Personnel.PersonnelID
         WHERE Personnel.OrganizationID IN (SELECT OrganizationID FROM OrganizationList)
               AND EventLog.EventID = @EventID)
         AS TotalPresent
FROM Organization
WHERE OrganizationID = @OrganizationID

【问题讨论】:

  • 问题是,SQL Server 中的递归 CTE 有一些限制。特别是递归部分不允许包含分组,因此不允许包含聚合。出于这个原因,我真的很想知道您对单个条目的解决方案,尤其是更高级别的解决方案可能是什么。你介意张贴吗?这里的某个人可能对如何将其开发为您的案例的完整解决方案有一个很好的想法。

标签: sql sql-server recursion count recursive-query


【解决方案1】:

我认为这对你有用:

WITH OrganizationTree (RootOrganizationID, OrganizationID)
AS
(
--Anchor
    SELECT  O.OrganizationID, O.OrganizationID
    FROM    Organization O
    UNION ALL
--Recurse
    SELECT  T.RootOrganizationID, O.OrganizationID
    FROM    OrganizationTree T
    JOIN    Organization O
        ON  O.ParentOrganizationId = T.OrganizationID
)
--execute
SELECT  P.OrganizationName, 
        SUM(ISNULL(PPL.NumberInOrganization, 0)) AS TotalNumberInOrganization, 
        SUM(ISNULL(EVT.NumberParticipatingInEvent, 0)) AS TotalNumberParticipatingInEvent
FROM    OrganizationTree T
JOIN    Organization P
        ON T.RootOrganizationID = P.OrganizationID
LEFT
JOIN    
(
        SELECT  P.OrganizationID, 
                COUNT(*) AS NumberInOrganization
        FROM    Personnel P
        GROUP BY P.OrganizationID
) PPL
        ON  PPL.OrganizationID = T.OrganizationID
LEFT
JOIN
(
        SELECT  P.OrganizationID,
                COUNT(*) AS NumberParticipatingInEvent
        FROM    EventLog EL
        JOIN    Personnel P
                ON  EL.PersonnelID = P.PersonnelID
        GROUP BY P.OrganizationID
) EVT
        ON  EVT.OrganizationID = T.OrganizationID
GROUP BY T.RootOrganizationID, P.OrganizationName

如果您需要像示例输出中那样缩进,那么这应该可以:

WITH OrganizationTree (RootOrganizationID, OrganizationID)
AS
(
--Anchor
    SELECT  O.OrganizationID, O.OrganizationID
    FROM    Organization O
    UNION ALL
--Recurse
    SELECT  T.RootOrganizationID, O.OrganizationID
    FROM    OrganizationTree T
    JOIN    Organization O
        ON  O.ParentOrganizationId = T.OrganizationID
)
--execute
SELECT  SPACE(L.OrganizationLevel) + P.OrganizationName AS FormattedOrganizationName,
        P.OrganizationName, 
        SUM(ISNULL(PPL.NumberInOrganization, 0)) AS TotalNumberInOrganization, 
        SUM(ISNULL(EVT.NumberParticipatingInEvent, 0)) AS TotalNumberParticipatingInEvent
FROM    OrganizationTree T
JOIN    
(
        SELECT  L.OrganizationID,
                (COUNT(*) - 1) AS OrganizationLevel
        FROM    OrganizationTree L
        GROUP BY L.OrganizationID
) L
    ON  T.RootOrganizationID = L.OrganizationID
JOIN    Organization P
        ON T.RootOrganizationID = P.OrganizationID
LEFT
JOIN    
(
        SELECT  P.OrganizationID, 
                COUNT(*) AS NumberInOrganization
        FROM    Personnel P
        GROUP BY P.OrganizationID
) PPL
        ON  PPL.OrganizationID = T.OrganizationID
LEFT
JOIN
(
        SELECT  P.OrganizationID,
                COUNT(*) AS NumberParticipatingInEvent
        FROM    EventLog EL
        JOIN    Personnel P
                ON  EL.PersonnelID = P.PersonnelID
        GROUP BY P.OrganizationID
) EVT
        ON  EVT.OrganizationID = T.OrganizationID
GROUP BY T.RootOrganizationID, L.OrganizationLevel, P.OrganizationName

【讨论】:

  • 我不确定您是否希望 TotalNumberParticipatingInEvent 是“参加一个或多个活动的单独个人的数量”或“参加每个活动的个人总和”。上面的查询是后者,但很容易变成前者 - 只需修改子查询...
  • 我不确定该技术是否有特定的名称——至少我不知道...!
猜你喜欢
  • 2014-12-19
  • 2012-12-05
  • 1970-01-01
  • 2014-10-10
  • 2011-02-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多