2013-10-09 54 views
1

我有一個非常複雜的查詢,我正在使用它的一部分檢索與產品關聯的類別。這些類別以遞歸方式存儲在Category表中。產品類別映射位於ProductCategory表中(技術上,單個產品可以有多個類別,但現在讓我們將其從表中除去,除非它是一個簡單的變量)。在遞歸SQL查詢中檢索特定級別

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 

我想詳細瞭解有關使用遞歸CTE的由@George Mavritsakis的意見,並決定嘗試實現它在功能和想出了這個更快的解決方案:

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

「ID列」是「身份」列......? –

+0

最頂級或第n類看到這個。 http://www.programmerinterview.com/index.php/database-sql/find-nth-highest-salary-sql/ –

+0

他們是身份專欄,我不認爲最頂級或nth將真正幫助這個由於遞歸的情況。 – cjbarth

回答