帮助从父子模型中的数据生成报告

时间:2010-03-01 12:41:59

标签: sql sql-server sql-server-2000 parent-child

我需要帮助解决有关保存在父子模型表中的数据以及我需要在其上构建的报告的问题。我已经尝试过搜索有关亲子问题的主题,但我在我的方案中找不到任何有用的东西。

我有什么

Microsoft SQL Server 2000数据库服务器。

一个categories表,其中有四列:category_idcategory_namefather_idvisible;这些类别有 x 根类别(其中 x 是可变的),并且可能 y 级别深(其中 y 是变量),如果一个类别是根级别,它有father_id null,否则它填充了父类别的id。

一个sales表,其中包含 z 列,其中一列为category_id,是categories.category_id的外键;销售必须始终具有类别,并且可以链接到上述 y 级别的任何位置。

我需要什么

我被问到一份报告,只显示根(第一级)类别,以及每个类别或其子项的销售物品数量,无论多深。即如果其中一个根类别为food,其中有一个名为fruit的子类别,其子类别名为apple,则我需要计算属于food的每个项目或fruitapple

您是否无法使用嵌套集数据模型?

我知道嵌套集模型,但我已经有了这样的表,并将它迁移到嵌套集模型会很痛苦(更不用说我甚至没有完全掌握嵌套集如何工作),不算数使用数据库的应用程序中所需的更改。 (如果有人认为这仍然是最不痛苦的方式,请解释为什么以及如何迁移当前数据。)

你不能使用CTE(通用表格式)吗?

不,它是Microsoft SQL Server 2000 ,并且 2005 版本中引入了公用表表达式。

先谢谢,安德烈。

2 个答案:

答案 0 :(得分:1)

基于SQL 2000的解决方案

DECLARE @Stack TABLE (
  StackID INTEGER IDENTITY
  , Category VARCHAR(20)
  , RootID INTEGER
  , ChildID INTEGER
  , Visited BIT)

INSERT INTO @Stack
SELECT  [Category] = c.category_name
        , [RootID] = c.category_id
        , [ChildID] = c.category_id
        , 0
FROM    Categories c

WHILE EXISTS (SELECT * FROM @Stack WHERE Visited = 0)
BEGIN
  DECLARE @StackID INTEGER
  SELECT  @StackID = MAX(StackID) FROM    @Stack

  INSERT INTO @Stack
  SELECT  st.Category
          , st.RootID
          , c.category_id
          , 0
  FROM    @Stack st
          INNER JOIN Categories c ON c.father_id = st.ChildID  
  WHERE   Visited = 0

  UPDATE  @Stack
  SET     Visited = 1
  WHERE   StackID <= @StackID
END

SELECT  st.RootID
        , st.Category
        , COUNT(s.sales_id)
FROM    @Stack st
        INNER JOIN Sales s ON s.category_id = st.ChildID
GROUP BY st.RootID, st.Category
ORDER BY st.RootID

基于SQL 2005的解决方案

CTE可以为您提供所需的内容

  • 从类别中选择每个类别作为根项目
  • 以递归方式添加每个根项目的每个子项
  • INNER JOIN您的销售表的结果。由于每个 root 都在CTE的结果中,因此简单的GROUP BY足以获得每个项目的计数。

SQL声明

;WITH QtyCTE AS (
  SELECT  [Category] = c.category_name
          , [RootID] = c.category_id
          , [ChildID] = c.category_id
  FROM    Categories c
  UNION ALL 
  SELECT  cte.Category
          , cte.RootID
          , c.category_id
  FROM    QtyCTE cte
          INNER JOIN Categories c ON c.father_id = cte.ChildID
)
SELECT  cte.RootID
        , cte.Category
        , COUNT(s.sales_id)
FROM    QtyCTE cte
        INNER JOIN Sales s ON s.category_id = cte.ChildID
GROUP BY cte.RootID, cte.Category
ORDER BY cte.RootID

答案 1 :(得分:0)

这样的东西?

CREATE TABLE #SingleLevelCategoryCounts
{ 
    category_id,
    count,
    root_id
}

CREATE TABLE #ProcessedCategories
{
    category_id,
    root_id
}

CREATE TABLE #TotalTopLevelCategoryCounts
{ 
    category_id,
    count
}

INSERT INTO #SingleLevelCategoryCounts
SELECT 
    category_id, SUM(*), category_id
FROM
    Categories 
    INNER JOIN Sales ON Categories.category_id = sales.category_id
WHERE
     Categories.father_id IS NULL
GROUP BY
     Categories.category_id


WHILE EXISTS (SELECT * FROM #SingleLevelCategoryCounts)
BEGIN
   IF NOT EXISTS(SELECT * FROM #TopLevelCategoryCounts)
   BEGIN
      INSERT INTO #TopLevelCategoryCounts
      SELECT
          root_id, count
      FROM
          #SingleLevelCategoryCounts
   END 
   ELSE
   BEGIN
       UPDATE top
       SET
          top.count = top.count + level.count
       FROM
          #TopLevelCategoryCounts top
          INNER JOIN #SingleLevelCategoryCounts level ON top.category_id = level.count
   END

   INSERT INTO #ProcessedCategories
   SELECT category_id, root_id FROM #SingleLevelCategoryCounts

   DELETE #SingleLevelCategoryCounts

   INSERT INTO #SingleLevelCategoryCounts
   SELECT 
        category_id, SUM(*), pc.root_id
   FROM
        Categories 
        INNER JOIN Sales ON Categories.category_id = sales.category_id
        INNER JOIN #ProcessedCategories pc ON Categories.father_id = pc.category_id
   WHERE
         Categories.category_id NOT IN
         (
             SELECT category_id in #ProcessedCategories
         )
   GROUP BY
         Categories.category_id


END