2009-06-16 5 views
3

OK, voici ce que j'essaie de faire. J'utilise une requête CTE dans MSSQL2005. L'objectif de la requête est de recréer via les relations parent-enfant des catégories de produits et de renvoyer le nombre de produits sous chaque catégorie (ceci comprend tous les produits contenus dans les catégories enfants)Requête récursive à l'aide de CTE dans SQL Server 2005

Ma version actuelle ne renvoie que le nombre de produits pour la catégorie en cours affiché. Cela ne tient pas compte des produits qui pourraient être contenus dans l'un de ses enfants.

La décharge de la base de données pour reproduire le problème, ainsi que la requête je et l'explication ci-dessous suit:

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 

Voici la requête CTE j'utilisais:

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 

D'abord, changer le " PC.ParentID "à 943. Vous verrez trois enregistrements renvoyés indiquant le nombre de produits pour chaque catégorie affichée.

Maintenant, changez le ParentID de à et re-exécuter. Vous verrez maintenant 1 résultat retourné appelé "Cardiologie", mais il montre 0 produits Dans cette catégorie, il y a des enfants (que vous avez déjà vu) qui contiennent des produits. Ma grande question est, à ce niveau (Parent 942) comment puis-je faire compter les produits contenus dans les enfants ci-dessous pour montrer 13 comme "ProductCount" Je pense que je pourrais avoir besoin d'une méthode de récursion de plus. J'ai essayé, mais je n'ai pas réussi.

Je suis ouvert à une procédure stockée qui ferait ce que je cherche. Je ne suis pas fixé d'une manière particulière. Donc, d'autres suggestions seraient appréciées.

Répondre

4

modifier OK après avoir lu en fait les exigences et la pensée un peu c'est en fait assez facile

Le point est que nous voulons deux choses (je pense!): La hiérarchie des catégories, et un nombre de produits. La hiérarchie se fait par un CTE récursive, et le comptage se fait en dehors que:

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

Les résultats sont les suivants:

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

Je suis redevable à this article by the inestimable Itzik Ben-Gan pour obtenir le coup d'envoi ma pensée. Son livre 'Inside MS SQL Server 2005: T-SQL Querying' est fortement recommandé.

+0

Après avoir lissé quelques autres erreurs, cette fondamentale reste: GROUP BY, HAVING, ou les fonctions d'agrégat ne sont pas permises dans la partie récursive d'une expression de table commune récursive 'ProductCategories' – Andomar

+0

Andomar, j'ai aussi reçu cette erreur en essayant la même chose . Apparemment, vous ne pouvez rien avoir d'autre que des sélections de base dans une requête récursive. –

+0

AakashM - cela fonctionne parfaitement, bien que la question est - comment limiter les résultats de la requête à tout sous 943? Ajout de la clause where comme la prochaine réponse suggère ne fonctionne pas tout à fait .. – Marty

2

Votre instruction WHERE limite le résultat à un parent. Si vous souhaitez voir tous les enfants inférieurs à 942, spécifiez 942 comme racine dans le CTE. Par exemple:

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 

Par ailleurs, les catégories CAN peuvent-elles avoir plusieurs parents? Si ce n'est pas le cas, envisagez d'éliminer la table Categories_XREF et de stocker ParentID dans la table Catégories.

+0

Andomar, oui, il peut y avoir plusieurs parents, sinon je ferais exactement ce que vous avez suggéré. –

Questions connexes