检索递归 SQL 查询中的特定级别

Posted

技术标签:

【中文标题】检索递归 SQL 查询中的特定级别【英文标题】:Retrieve specific level in recursive SQL query 【发布时间】:2013-10-09 13:30:53 【问题描述】:

我正在处理一个非常复杂的查询,其中一部分必须检索与产品关联的类别。这些类别递归地存储在Category 表中。产品-类别映射在ProductCategory 表中(从技术上讲,一个产品可以有多个类别,但现在让我们把它放在表之外,除非它是一个需要考虑的简单变量)。

Category 表非常简单。一列是CategoryID,另一列是ParentCategoryID,第三列是Name。由此,类别是嵌套的。 ProductCategory 表也很简单。一列是ProductID,另一列是CategoryID

我需要检索任何给定产品的最高和第二最高类别。然后我在报告中使用这些信息并进行一些分析。我的解决方案真的很慢并且不能很好地扩展。我不知道如何才能更有效地提取我需要的数据。

我的解决方案试图做的是将作为特定产品指定类别的父级的所有类别收集在一起,然后抓取我找到的最后两个并将它们返回。我将其作为一个标量函数完成,我发送当前的CategoryID 和我想要返回的级别,因此一个调用为 0,另一个调用为 1。

我的示例代码:

WITH Categories AS (
    SELECT DISTINCT
        CategoryID
    FROM
        ProductCategory
), CategoriesAtDepth AS (
    SELECT
        Categories.CategoryID
        , dbo.WR_f_GetCategoryIDAtDepth(Categories.CategoryID, 0) AS TopCategory
        , dbo.WR_f_GetCategoryIDAtDepth(Categories.CategoryID, 1) AS SecondCategory
    FROM
        Categories
)
SELECT
    CategoriesAtDepth.CategoryID
    , c1.Name AS TopCategory
    , c2.Name AS SecondCategory
FROM
    CategoriesAtDepth LEFT JOIN
    Category AS c1 ON CategoriesAtDepth.TopCategory = c1.CategoryID LEFT JOIN
    Category AS c2 ON CategoriesAtDepth.SecondCategory = c2.CategoryID

及功能码:

CREATE FUNCTION WR_f_GetCategoryIDAtDepth 
(
    @CategoryID AS int
    ,@Depth AS int = 0
)
RETURNS int
AS
BEGIN

    -- Declare the return variable here
    DECLARE @Result int
    DECLARE @CurrentHeight int = 0
    DECLARE @CurrentCategoryID int = @CategoryID
    DECLARE @CategoryLevels table
    (
        Height int
        ,CategoryID int
    )

    BEGIN
        --Populate a table with all the categoy IDs in the chain
        WHILE @CurrentCategoryID > 0
        BEGIN
            INSERT INTO @CategoryLevels (Height, CategoryID) VALUES (@CurrentHeight + 1, @CurrentCategoryID)
            SET @CurrentCategoryID = (SELECT ParentCategoryID FROM Category WHERE CategoryID = ISNULL((SELECT CategoryID FROM @CategoryLevels WHERE Height = @CurrentHeight + 1), 0))
            SET @CurrentHeight = @CurrentHeight + 1
        END
        SET @Result = (SELECT CategoryID FROM @CategoryLevels WHERE Height = (@CurrentHeight - @Depth))
    END

    -- Return the result of the function
    RETURN @Result

END
GO

我更多地考虑了@George Mavritsakis 关于使用递归 CTE 的评论,并决定尝试在函数中实现它并想出了这个更快更快的解决方案:

CREATE FUNCTION WR_f_GetCategoryIDAtDepth 
(
    @CategoryID AS int
    ,@Depth AS int = 0
)
RETURNS int
AS
BEGIN

    -- Declare the return variable here
    DECLARE @Result int
    DECLARE @CategoryLevels table
    (
        Height int
        ,CategoryID int
    )

    BEGIN
        --Populate a table with all the categoy IDs in the chain
        WITH Base AS (
            SELECT
                0 AS Height
                , @CategoryID AS CategoryID

            UNION ALL

            SELECT
                Height + 1
                , ParentCategoryID
            FROM
                Category INNER JOIN
                    Base ON Category.CategoryID = Base.CategoryID
        )
        INSERT INTO @CategoryLevels (Height, CategoryID)
            SELECT * FROM Base

        SET @Result = (SELECT CategoryID FROM @CategoryLevels WHERE Height = ((SELECT MAX(Height) FROM @CategoryLevels) - @Depth - 1))
    END

    -- Return the result of the function
    RETURN @Result

END
GO

【问题讨论】:

ID columnsidentity 列...?? 对于***或第 n 个类别,请参阅此。 programmerinterview.com/index.php/database-sql/… 它们是标识列,由于递归,我认为 top-most 或 nth 在这种情况下不会真正有帮助。 【参考方案1】:

您必须查看递归 CTE:http://technet.microsoft.com/en-us/library/ms186243%28v=sql.105%29.aspx

您的解决方案很慢,因为您使用函数 WR_f_GetCategoryIDAtDepth 多次查询 Category 表。

【讨论】:

这是我的问题:我怎样才能获得递归查询以递归到特定点?

以上是关于检索递归 SQL 查询中的特定级别的主要内容,如果未能解决你的问题,请参考以下文章

递归sql级别1出现错误

oracle 表删除出错 ORA-00604: 递归 SQL 级别 1 出现错误 ORA-01426: 数字溢出

jdbc 查询5万条数据出现 ORA-00604: 递归 SQL 级别 1 出现错误 ORA-01000: 超出打开游标的最大数

如何在 Oracle SQL 中检索父行的所有递归子代?

sql:如何存储树数据并进行递归遍历(通过查询)?

sql server 递归查询