2009-06-16 8 views
3

ОК, вот что я пытаюсь сделать. Я использую запрос CTE в MSSQL2005. Цель запроса состоит в том, чтобы реорганизовать дочерние отношения с родителями по категориям продуктов и вернуть количество продуктов под каждую категорию (включая любые продукты, содержащиеся в детских категориях).Рекурсивный запрос с использованием CTE в SQL Server 2005

Моя текущая версия возвращает только количество товаров для категории отображается. Это не учет продуктов, которые могут содержаться в любом из его детей.

Свалка базы данных, чтобы воспроизвести проблему, наряду с запросом я использовал и объяснение следует ниже:

CREATE TABLE [Categories] (
    [CategoryID] INT, 
    [Name] NCHAR(150) 

    ) 
    GO 

/* Data for the `Query_Result` table (Records 1 - 5) */ 


INSERT INTO [Categories] ([CategoryID], [Name]) 
VALUES (942, N'Diagnostic Equipment') 
GO 

INSERT INTO [Categories] ([CategoryID], [Name]) 
VALUES (943, N'Cardiology') 
GO 

INSERT INTO [Categories] ([CategoryID], [Name]) 
VALUES (959, N'Electrodes') 
GO 

INSERT INTO [Categories] ([CategoryID], [Name]) 
VALUES (960, N'Stress Systems') 
GO 

INSERT INTO [Categories] ([CategoryID], [Name]) 
VALUES (961, N'EKG Machines') 
GO 

CREATE TABLE [Categories_XREF] (
    [CatXRefID] INT, 
    [CategoryID] INT, 
    [ParentID] INT 
) 
GO 


/* Data for the `Query_Result` table (Records 1 - 5) */ 


INSERT INTO [Categories_XREF] ([CatXRefID], [CategoryID], [ParentID]) 
VALUES (827, 942, 0) 
GO 

INSERT INTO [Categories_XREF] ([CatXRefID], [CategoryID], [ParentID]) 
VALUES (828, 943, 942) 
GO 

INSERT INTO [Categories_XREF] ([CatXRefID], [CategoryID], [ParentID]) 
VALUES (928, 959, 943) 
GO 

INSERT INTO [Categories_XREF] ([CatXRefID], [CategoryID], [ParentID]) 
VALUES (929, 960, 943) 
GO 

INSERT INTO [Categories_XREF] ([CatXRefID], [CategoryID], [ParentID]) 
VALUES (930, 961, 943) 
GO 


CREATE TABLE [Products_Categories_XREF] (
    [ID] INT, 
    [ProductID] INT, 
    [CategoryID] INT 
) 
GO 


/* Data for the `Query_Result` table (Records 1 - 13) */ 


INSERT INTO [Products_Categories_XREF] ([ID], [ProductID], [CategoryID]) 
VALUES (252065, 12684, 961) 
GO 

INSERT INTO [Products_Categories_XREF] ([ID], [ProductID], [CategoryID]) 
VALUES (252066, 12685, 959) 
GO 

INSERT INTO [Products_Categories_XREF] ([ID], [ProductID], [CategoryID]) 
VALUES (252067, 12686, 960) 
GO 

INSERT INTO [Products_Categories_XREF] ([ID], [ProductID], [CategoryID]) 
VALUES (252068, 12687, 961) 
GO 

INSERT INTO [Products_Categories_XREF] ([ID], [ProductID], [CategoryID]) 
VALUES (252128, 12738, 961) 
GO 

INSERT INTO [Products_Categories_XREF] ([ID], [ProductID], [CategoryID]) 
VALUES (252129, 12739, 959) 
GO 

INSERT INTO [Products_Categories_XREF] ([ID], [ProductID], [CategoryID]) 
VALUES (252130, 12740, 959) 
GO 

INSERT INTO [Products_Categories_XREF] ([ID], [ProductID], [CategoryID]) 
VALUES (252131, 12741, 959) 
GO 

INSERT INTO [Products_Categories_XREF] ([ID], [ProductID], [CategoryID]) 
VALUES (252132, 12742, 959) 
GO 

INSERT INTO [Products_Categories_XREF] ([ID], [ProductID], [CategoryID]) 
VALUES (252133, 12743, 959) 
GO 

INSERT INTO [Products_Categories_XREF] ([ID], [ProductID], [CategoryID]) 
VALUES (252134, 12744, 959) 
GO 

INSERT INTO [Products_Categories_XREF] ([ID], [ProductID], [CategoryID]) 
VALUES (252135, 12745, 959) 
GO 

INSERT INTO [Products_Categories_XREF] ([ID], [ProductID], [CategoryID]) 
VALUES (252136, 12746, 959) 
GO 

CREATE TABLE [Products] (
    [ProductID] INT 
) 
GO 


/* Data for the `Query_Result` table (Records 1 - 13) */ 


INSERT INTO [Products] ([ProductID]) 
VALUES (12684) 
GO 

INSERT INTO [Products] ([ProductID]) 
VALUES (12685) 
GO 

INSERT INTO [Products] ([ProductID]) 
VALUES (12686) 
GO 

INSERT INTO [Products] ([ProductID]) 
VALUES (12687) 
GO 

INSERT INTO [Products] ([ProductID]) 
VALUES (12738) 
GO 

INSERT INTO [Products] ([ProductID]) 
VALUES (12739) 
GO 

INSERT INTO [Products] ([ProductID]) 
VALUES (12740) 
GO 

INSERT INTO [Products] ([ProductID]) 
VALUES (12741) 
GO 

INSERT INTO [Products] ([ProductID]) 
VALUES (12742) 
GO 

INSERT INTO [Products] ([ProductID]) 
VALUES (12743) 
GO 

INSERT INTO [Products] ([ProductID]) 
VALUES (12744) 
GO 

INSERT INTO [Products] ([ProductID]) 
VALUES (12745) 
GO 

INSERT INTO [Products] ([ProductID]) 
VALUES (12746) 
GO 

Вот запрос CTE я использовал:

WITH ProductCategories (CategoryID, ParentID, [Name], Level) 
AS 
(
-- Anchor member definition 
    SELECT 
    C.CategoryID, 
    CXR.ParentID, 
    C.Name, 
    0 AS Level 
    FROM 
    Categories C, 
    Categories_XRef CXR 
    WHERE 
    C.CategoryID = CXR.CategoryID 
    AND CXR.ParentID = 0 
    UNION ALL 
-- Recursive member definition 
SELECT 
    C.CategoryID, 
    CXR.ParentID, 
    C.Name, 
    Level + 1 
    FROM 
    Categories C, 
    Categories_XRef CXR, 
    ProductCategories AS PC 
    WHERE 
    C.CategoryID = CXR.CategoryID 
    AND CXR.ParentID = PC.CategoryID 

) 
SELECT 
    PC.ParentID, 
    PC.CategoryID, 
    PC.Name, 
    PC.Level, 
    (SELECT 
     Count(P.ProductID) 
    FROM 
     Products P, 
     Products_Categories_XREF PCXR 
     WHERE 
     P.ProductID = PCXR.ProductID 
     AND PCXR.CategoryID = PC.CategoryID 
    ) as ProductCount 
FROM  
    Categories C, 
    ProductCategories PC 
WHERE 
PC.CategoryID = C.CategoryID 
AND PC.ParentID = 943 
ORDER BY 
    Level, PC.Name 

Во-первых, изменить " PC.ParentID "до 943. Вы увидите три возвращенные записи, показывающие количество товаров для каждой отображаемой категории.

Теперь измените ParentID от в и повторно запустить его. Теперь вы увидите 1 результат, получивший название «Кардиология», но он показывает 0 продуктов Под этой категорией есть дети (которые вы ранее видели), которые содержат продукты. Мой большой вопрос: на этом уровне (родительский 942), как я могу заставить его подсчитать продукты, содержащиеся в приведенных ниже дочерних элементах, чтобы показать 13 как «ProductCount». Я, как мне кажется, может потребоваться еще один метод рекурсии. Я пробовал это, но не имел успеха.

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

ответ

4

редактировать OK будучи на самом деле читать требования и мысли немного это на самом деле довольно легко

Дело в том, что мы хотим две вещи (я думаю!): В иерархии категорий, и количество продуктов. Иерархия осуществляется с помощью рекурсивного CTE, и подсчет производится за пределами этого:

-- The CTE returns the cat hierarchy: 
-- one row for each ancestor-descendant relationship 
-- (including the self-relationship for each category) 
WITH CategoryHierarchy AS (
    -- Anchor member: self relationship for each category 
    SELECT CategoryID AS Ancestor, CategoryID AS Descendant 
    FROM Categories 
UNION ALL 
    -- Recursive member: for each row, select the children 
    SELECT ParentCategory.Ancestor, Children.CategoryID 
    FROM 
     CategoryHierarchy AS ParentCategory 
     INNER JOIN Categories_XREF AS Children 
     ON ParentCategory.Descendant = Children.ParentID 
) 
SELECT CH.Ancestor, COUNT(ProductID) AS ProductsInTree 
-- outer join to product-categories to include 
-- all categories, even those with no products directly associated 
FROM CategoryHierarchy CH 
LEFT JOIN Products_Categories_XREF PC 
ON CH.Descendant = PC.CategoryID 
GROUP BY CH.Ancestor 

Результаты:

Ancestor ProductsInTree 
----------- -------------- 
942   13 
943   13 
959   9 
960   1 
961   3 

Я обязан this article by the inestimable Itzik Ben-Gan для получения моего мышления кик-начал. Его книга «Внутри MS SQL Server 2005: T-SQL Querying» настоятельно рекомендуется.

+0

После сглаживания некоторых других ошибок этот фундаментальный остается: функции GROUP BY, HAVING или aggregate не разрешены в рекурсивной части рекурсивного общего выражения таблицы «ProductCategories» – Andomar

+0

Andomar, я тоже получил эту ошибку, попробовав то же самое , По-видимому, вы не можете иметь ничего, кроме базовых выборок в рекурсивном запросе. – 2009-06-17 01:20:47

+0

AakashM - это отлично работает, хотя вопрос заключается в том, как ограничить результаты запроса ко всему под 943? Добавление предложения where в качестве следующего ответа подсказывает, что он не совсем работает. – Marty

2

Ваш оператор WHERE ограничивает результат одним родителем. Если вы хотите видеть всех детей ниже 942, укажите 942 в качестве корня в CTE. Например:

WITH CTE (CategoryID, ParentID, [Name], [Level]) 
AS 
(
    SELECT C.CategoryID, CXR.ParentID, C.Name, 0 AS Level 
    FROM Categories C 
    INNER JOIN Categories_XRef CXR ON C.CategoryID = CXR.CategoryID 
    WHERE CXR.CategoryID = 943 
    UNION ALL 
    SELECT C.CategoryID, CXR.ParentID, C.Name, Level + 1 
    FROM Categories C 
    INNER JOIN Categories_XRef CXR ON C.CategoryID = CXR.CategoryID 
    INNER JOIN CTE PC ON PC.CategoryID = CXR.ParentID 
) 
SELECT * FROM CTE 

К слову, категории могут иметь несколько родителей? Если нет, подумайте об исключении таблицы Categories_XREF и сохранении ParentID в таблице «Категории».

+0

Андомар, да, может быть несколько родителей, иначе я бы сделал именно то, что вы предложили. – 2009-06-17 01:18:52

Смежные вопросы