2009-06-16 5 views
3

OK, hier ist, was ich versuche zu tun. Ich verwende eine CTE-Abfrage in MSSQL2005. Das Ziel der Abfrage besteht darin, über Eltern-Kind-Beziehungen von Produktkategorien zu rekrutieren und die Anzahl der Produkte in jeder Kategorie zurückzugeben (dies schließt alle Produkte in untergeordneten Kategorien ein).Rekursive Abfrage mit CTE in SQL Server 2005

Meine aktuelle Version gibt nur die Produktzahl für die Kategorie zurück angezeigt. Es werden keine Produkte berücksichtigt, die möglicherweise in einem der untergeordneten Elemente enthalten sind.

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 

Hier ist der CTE-Abfrage Ich war mit:

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 

Erstens ändern sich die "

Die Datenbank-Dump das Problem, zusammen mit der Abfrage, die ich verwendet, und Erklärung folgt unten zu reproduzieren PC.ParentID "an 943. Sie sehen drei Datensätze, die die Produktanzahl für jede angezeigte Kategorie anzeigen. Jetzt

, ändern Sie die ParentID von zu und starten Sie es. Sie sehen nun 1 zurückgegebenes Ergebnis mit dem Namen "Kardiologie", aber es werden 0 Produkte angezeigt. Unter dieser Kategorie befinden sich Kinder (die Sie zuvor gesehen haben), die Produkte enthalten. Meine große Frage ist, auf dieser Ebene (Eltern 942), wie kann ich die Produkte in den Kindern darunter zählen, um 13 als "ProductCount" zu zeigen. Ich denke irgendwie, dass ich eine weitere Rekursionsmethode brauche. Ich habe das versucht, hatte aber keinen Erfolg.

Ich bin offen für eine gespeicherte Prozedur, die tun würde, was ich suche. Ich bin nicht auf einen bestimmten Weg festgelegt. So würden alle anderen Vorschläge geschätzt werden.

Antwort

4

bearbeiten OK hat tatsächlich die Anforderungen gelesen und dachte, ein bisschen das eigentlich ganz einfach ist

Der Punkt ist, dass wir zwei Dinge wollen (ich denke!): Die Kategoriehierarchie, und eine Anzahl von Produkten. Die Hierarchie wird durch eine rekursive CTE getan, und das Zählen außerhalb getan:

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

Die Ergebnisse sind:

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

verdanke ich this article by the inestimable Itzik Ben-Gan für immer mein Denken Kick gestartet. Sein Buch 'Inside MS SQL Server 2005: T-SQL-Abfrage' wird dringend empfohlen.

+0

Nachdem einige andere Fehler geglättet wurden, bleibt dieser grundlegende erhalten: GROUP BY-, HAVING- oder Aggregatfunktionen sind im rekursiven Teil eines rekursiven allgemeinen Tabellenausdrucks 'ProductCategories' nicht erlaubt. – Andomar

+0

Andomar, ich habe diesen Fehler auch durch das gleiche Ergebnis erhalten . Anscheinend können Sie in einer rekursiven Abfrage nichts anderes als basic selects haben. –

+0

AakashM - das funktioniert perfekt, obwohl die Frage ist - wie die Ergebnisse der Abfrage auf alles unter 943 beschränken? Das Hinzufügen der Where-Klausel, wie die nächste Antwort suggeriert, funktioniert nicht ganz. – Marty

2

Ihre WHERE-Anweisung begrenzt das Ergebnis auf ein Elternteil. Wenn Sie alle Kinder unter 942 sehen möchten, geben Sie 942 als Wurzel im CTE an. Zum Beispiel:

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 

Übrigens, können Kategorien mehrere Eltern haben? Wenn nicht, sollten Sie die Tabelle "Categories_XREF" entfernen und ParentID in der Tabelle "Categories" speichern.

+0

Andomar, ja, es kann mehrere Eltern geben, sonst würde ich genau das tun, was Sie vorgeschlagen haben. –