存储过程选择类别及其后代中的项目

时间:2013-09-19 01:56:03

标签: sql sql-server stored-procedures

我有这3张桌子

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

如你所见

  • ProductsCategories是多对多的
  • Category可能有也可能没有父母(父母也是类别)

如何在类别(包括其后代)

下创建选择所有(不同)产品的存储过程

我想要的伪代码(不是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

1 个答案:

答案 0 :(得分:1)

编辑:我以前无处接近。添加了功能示例

编辑2:更新了小提琴以获得更清晰的程序并更改了代码以反映正确的解决方案

这个问题需要一个递归过程来深入挖掘后代树的未知深度。这有可能是一个非常昂贵的操作,如果两个类别互相称为父母,这里的设计也可能导致无限循环,所以你可能想要加入一些保护措施。

我使用临时表来收集所有不同查询的结果,然后使用调用递归过程的过程返回它们,因此所需要的只是调用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更有经验,但这在小提琴中起作用。由于它使用了递归,游标和额外的表,你肯定会想要评估它的性能限制,但它确实可以处理我提供的数据。

This is the fiddle