0

我有这 3 张桌子

Products (Id, Name)
Products_Categories (ProductId, CategoryId)
Categories (Id, ParentCategoryId)

如你看到的

  • ProductstoCategories是多对多
  • ACategory可能有也可能没有父级(父级也是一个类别)

如何创建一个存储过程来选择一个类别下的所有(不同的)产品(包括其后代)

我想要的示例伪代码(不是 LINQ):

Category c;
return c.GetSelfAndDescendants().Select(x => x.Products).Distinct();

我需要的是存储过程,例如。

CREATE PROCEDURE [spAllProductsUnderCateg] 
    @categId int 
AS
BEGIN
    SET NOCOUNT ON;

    -- This may be completely wrong
    SELECT DISTINCT * FROM Products p
    JOIN Products_Categories ON Products_Categories.ProductId = p.Id
    WHERE...???
END
GO
4

1 回答 1

1

编辑:我之前离得很近。添加了功能示例的小提琴

Edit2:更新小提琴以获得更清洁的程序并更改代码以反映正确的解决方案

这个问题需要一个递归过程来深入挖掘后代树的未知深度。这有可能是一个非常昂贵的操作,并且如果两个类别相互引用为父级,那么这里设计的也可能导致无限循环,因此您可能需要加入一些保护措施来防止这种情况发生。

我使用一个临时表来收集所有不同查询的结果,然后将它们与调用递归过程的过程一起返回,因此只需调用 spAllProductsUnderCateg 即可返回结果集。

CREATE PROCEDURE [spAllProductsUnderCateg] 
    @categId int 
AS
BEGIN

    CREATE TABLE #ProductsByCategory (Name varchar(100))

    -- This may be completely wrong
    INSERT INTO #ProductsByCategory 
      SELECT DISTINCT p.Name FROM Products p
      JOIN Products_Categories pc ON pc.ProductId = p.Id
      WHERE pc.CategoryId = @categId

    EXEC spGetChildCategories @categId

    SELECT DISTINCT * FROM #ProductsByCategory

END
GO

CREATE PROCEDURE [spGetChildCategories]
    @categId int
AS
BEGIN
    DECLARE @nextCatId int
    DECLARE cur CURSOR LOCAL
    FOR SELECT DISTINCT Id FROM Categories
    WHERE ParentCategoryId = @categId

    OPEN cur
    FETCH NEXT FROM cur INTO @nextCatId
    WHILE @@FETCH_STATUS = 0
      BEGIN
         INSERT INTO #ProductsByCategory 
         SELECT DISTINCT p.Name FROM Products p
         JOIN Products_Categories pc ON pc.ProductId = p.Id
         WHERE pc.CategoryId = @nextCatId

         EXEC spGetChildCategories @nextCatId

         FETCH NEXT FROM cur INTO @nextCatId
      END
    CLOSE cur
    END
GO

我在 PL/SQL 方面比 T-SQL 更有经验,但这在小提琴中起作用。由于它使用递归、游标和一个额外的表,您肯定会想要评估它的性能限制,但它确实适用于我提供的数据。

这是小提琴

于 2013-09-19T02:12:22.010 回答