【问题标题】:Recursive Query using CTE in SQL Server 2005在 SQL Server 2005 中使用 CTE 进行递归查询
【发布时间】:2010-11-03 11:37:52
【问题描述】:

好的,这就是我想要做的。我在 MSSQL2005 中使用 CTE 查询。查询的目的是通过产品类别的父子关系进行递归,并返回每个类别下的产品数量(这包括子类别中包含的任何产品)

我当前的版本只返回所显示类别的产品数量。它不考虑可能包含在其任何子代中的产品。

重现问题的数据库转储,以及我使用的查询和解释如下:

    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 从 943 更改为 942 并重新运行它。您现在会看到返回 1 个名为“Cardiology”的结果,但它显示 0 个产品 在此类别下,有包含产品的儿童(您之前看到的)。我的大问题是,在这个级别(父级 942)我怎样才能让它计算下面子级中包含的产品,以将 13 显示为“ProductCount”我有点想我可能需要一种更多的递归方法。我试过了,但没有成功。

我对可以执行我正在寻找的存储过程持开放态度。我没有设置在一种特定的方式上。因此,任何其他建议将不胜感激。

【问题讨论】:

    标签: sql-server tsql recursion common-table-expression


    【解决方案1】:

    您的 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 存储在 Categories 表中。

    【讨论】:

    • Andomar,是的,可以有多个父母,否则我会按照你的建议做。
    【解决方案2】:

    edit 好的,实际上已经阅读了要求并认为这实际上很容易(我认为!)

    关键是我们需要两件事:类别层次结构和产品数量。层次结构由递归 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 查询”。

    【讨论】:

    • 在平滑了一些其他错误之后,这个基本错误仍然存​​在:递归公用表表达式“ProductCategories”的递归部分中不允许使用 GROUP BY、HAVING 或聚合函数
    • Andomar,我也通过尝试同样的事情收到了这个错误。显然,在递归查询中,除了基本选择之外,您什么都不能拥有。
    • AakashM - 这很完美,但问题是 - 如何将查询结果限制为 943 以下的所有内容?添加 where 子句作为下一个答案建议并不完全有效..
    • @AakashM - 谢谢。通过在顶部添加另一个 CTE,它只返回所需的子树(使用 MSDN 中的任何 CTE 示例)然后使用上面示例中的结果,而不是“来自类别”(替换为“来自 subtreeCTE ")
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-25
    • 2012-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多