Parent Id 不在列表中时递归 CTE

Recursive CTE while Parent Id not in a List

我有以下嵌套集

结果就是这棵树

1 -
  |---- 2 -
  |       |---- 4 -
  |               |---- 7
  |               |---- 8
  |----10 -
          |---- 9
3 -
  |----  5
  |----  6
13-
  |---- 11
  |---- 12

我有一份产品清单 SELECT 身份证、姓名... 来自产品

与类别的 many-to-many 关系。所有类别都可以有促销。好的,问题来了。

假设我在类别 7、8、6 中有一个 ProductX。以及类别 1、2、3 中的促销。我需要获得最接近的 parent 每个类别的促销,或者直到没有更多 parent 为止。

最终结果应该是

CategoryId PromotionPrice
    2          price...
    3          price...

我有什么

WITH Promotions (CategoryId, PromotionPrice)
{
    SELECT CategoryId, PromotionPrice
    FROM Promotions
}
SELECT CategoryId, PromotionPrice
FROM NestedSet s1
    LEFT JOIN NestedSet s2 ON s1.ParentId = s2.Id
    LEFT JOIN Promotions p ON s1.CategoryId = p.CategoryId

然后获取 Better Promotion(我知道该怎么做)并应用于主查询 SELECT * FROM Products;对于每个产品(所以只是一个简单的连接)。

我的问题是我知道我需要使用(或者我认为我需要使用)递归 CTE,但我不知道该怎么做。因为它应该只对每一行递归,并且只在它找到该行的促销之前。

编辑(我将尝试解释其中的逻辑)。

ProductId  CategoryId
     1         7
     1         8
     1         6

该产品有 2 个直接 parent:4 个(来自 7 和 8)和 3 个(来自 6) 我在 CategoryIds 中有促销活动:1、2、3。 第一轮查询结果

CategoryId ParentId PromotionPrice
     7         4         NULL
     8         4         NULL 
     6         3          10

重要的是 ParentId,所以我可以 GroupBy ParentId,结果将是

CategoryId PromotionPrice
     4         NULL
     3          10

好的,因为 promotionPrice 是 NULL 我需要去他的 parent(在本例中是 2)所以上面的查询需要 return

CategoryId ParentId PromotionPrice
     4       2         NULL
     3      NULL       10

由于 PromotionPrice 为 Null,我必须检查是否有针对 Category2 的促销,因此结果为

CategoryId ParentId PromotionPrice
     2       1         15
     3      NULL       10

到此为止。如果我从 Category2 中删除促销活动,它应该再进行一轮:

CategoryId ParentId PromotionPrice
     1      NULL       5
     3      NULL       10

在这一点上,因为没有更多的 parents,PromotionPrice 是否为 null 并不重要。问题是我需要一直努力寻找升职机会。

因为我正在查看 SortPath 已经拥有所有信息,只需要将其分解并递归地向后查找直到找到具有促销的 ID,我仍然对如何实现这一点一无所知。

希望这对解释有所帮助。

注意:我稍作编辑以反映您提供的样本数据。

设置

这是我必须代表您的嵌套集的内容:

declare @nestedSet table (
    id int,
    parentId int
);

insert @nestedSet values 
    (1, null), (2, 1), (4, 2), (7, 4), (8, 4), (10, 1), (9, 10), (1004, 1),
    (3, null), (5, 3), (6, 3),
    (13, null), (11, 13), (12, 13);

这是我为您的促销活动制作的:

declare @promotions table (
    promotionId int identity(1,1),
    categoryId int,
    price float
);

insert @promotions values (1, 5), (2, 15), (3, 10);

还有您的产品,我已将其重命名为 productCategories 以更好地反映其内容:

declare @productCategories table (productId int, categoryId int);
insert @productCategories values (1,7),(1,8),(1,6);

解决方案

作为主播,刚刚拉进了产品table。但我认为在您的用例中,您需要一个过滤器来挑选出合适的基础产品。然后我做了一个计算来检查该类别是否已经是促销活动。如果是,那么它代表一个叶节点。

在递归中,我只是为每个不是叶子的节点向上移动了嵌套集的层次结构。我又做了一次计算,看分类是不是推广,看是不是叶节点。

从结果中,我选择了所有的叶子节点,按价格排序,输出最前面的那个。

declare @productId int = 1;

with

    traverse as (

        select      categoryId, 
                    parentId, 

                    isLeaf = iif(exists (
                        select 0  
                        from @promotions pm 
                        where pd.categoryId = pm.categoryId
                    ), 1, 0)

        from        @productCategories pd
        join        @nestedSet n on pd.categoryId = n.id
        where       pd.productId = @productId 

        union all
        select      categoryId = par.id,
                    par.parentId,

                    isLeaf = iif(exists (
                        select 0 
                        from @promotions pm 
                        where par.id = pm.categoryId
                    ), 1, 0)

        from        traverse pd
        join        @nestedSet par on pd.parentId = par.id
        where       pd.isLeaf = 0

    )


    select      
    top 1       p.*
    from        traverse t
    join        @promotions p on t.categoryId = p.categoryId
    where       isLeaf = 1
    order by    p.price