Помощь в создании отчета по данным в модели «родители-дети» - PullRequest
1 голос
/ 01 марта 2010

Мне нужна помощь в решении проблемы, связанной с данными, сохраненными в таблице моделей «родители-дети», и отчетом, который мне нужно построить. Я уже пробовал искать темы о проблемах родителей и детей, но не смог найти ничего полезного в моем сценарии.

Что у меня есть

Сервер базы данных Microsoft SQL Server 2000.

A categories таблица, которая имеет четыре столбца: category_id, category_name, father_id и visible; категории имеют x корневые категории (где x является переменной) и могут иметь глубину y (где y является переменной), если категория относится к корневому уровню, она имеет father_id ноль, иначе она заполняется идентификатором категории отца.

A sales таблица, в которой есть z столбцов, один из которых category_id, внешний ключ для categories.category_id; продажа всегда должна иметь категорию, и она может быть связана в любом месте вышеупомянутого уровня y .

Что мне нужно

Мне был предложен отчет, отображающий только корневые категории (первого уровня) и количество торговых принадлежностей для каждого из них или их детей, независимо от их глубины. То есть если одной из корневых категорий является food, которая имеет дочернюю категорию с именем fruit, которая имеет дочернюю категорию с именем apple, мне нужно сосчитать каждый элемент, принадлежащий food или fruit или apple .

Не могли бы вы использовать модель данных с вложенным набором?

Я знаю модель вложенного набора, но у меня уже есть таблица таким образом, и перенос ее на модель вложенного набора будет проблематичным (не говоря уже о том, что я даже не полностью понял, как работает вложенный набор), не считая изменения, необходимые в приложении с использованием базы данных. (Если кто-то считает, что это по-прежнему наименее болезненный способ, объясните, почему и как текущие данные могут быть перенесены.)

Не могли бы вы использовать CTE (общие табличные выражения)?

Нет, это Microsoft SQL Server 2000 , а общие табличные выражения представлены в 2005 редакции.

Заранее спасибо, Андреа.

Ответы [ 2 ]

1 голос
/ 01 марта 2010

Решение на основе 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

A 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
0 голосов
/ 01 марта 2010

Как то так?

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
...