0

我有两张桌子;类别和产品。对于每个类别,我想计算其所有子类别中有多少产品。我已经数过了每个类别有多少。示例表是:

分类:

ID  ParentID  ProductCount  SubCategoryProducts
1   NULL      0
2   1         2
3   2         1

产品:

ProductID  CategoryID
123        2
124        2
125        3

所以我希望我的功能是:

ID  ParentID  ProductCount  SubCategoryProducts
1   NULL      0             3
2   1         2             1
3   2         1             0

它只需要作为一个选择查询,不需要更新数据库。

有任何想法吗?

编辑:SQL Fiddle:http ://sqlfiddle.com/#!2/1941a/4/0

4

4 回答 4

1

如果是我,我会创建一个存储过程。另一种选择是通过第一个查询循环使用 PHP,然后为每个 ID 运行另一个查询 - 但这种逻辑会大大减慢您的页面速度。

这是一个关于存储过程的很好的教程:http: //net.tutsplus.com/tutorials/an-introduction-to-stored-procedures/

基本上你运行我上面提到的与 PHP 相同的循环(但它运行得更快)。该过程存储在数据库中,可以像函数一样调用。结果与查询相同。

根据要求,在我的实例中,这是一个示例过程(或者更确切地说,它使用了两个),“ags_orgs”的行为方式与您的具有 parentOrgID 的类别类似。“getChildOrgs”也有点像一个冗余函数,因为我不知道我必须下降多少级(这是为 MSSQL 编写的 - 可能与 mySQL 存在差异)不幸的是,这不计算行数,而是获取数据. 我强烈建议您学习一两个教程,以更好地了解它的工作原理:

USE [dbname]
GO

/****** Object:  StoredProcedure [dbo].[getChildOrgs]    Script Date: 09/26/2012 15:30:06 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE PROCEDURE [dbo].[getChildOrgs]

@myParentID int,
@isActive tinyint = NULL

AS
BEGIN

    SET NOCOUNT ON
    DECLARE @orgID int, @orgName varchar(255), @level int

        DECLARE cur CURSOR LOCAL FOR SELECT orgID FROM dbo.ags_orgs WHERE parentOrgID = @myParentID AND isActive = ISNULL(@isActive, isActive) ORDER BY orderNum, orgName


    OPEN cur
        fetch next from cur into @orgID
    WHILE @@fetch_status = 0
    BEGIN
        INSERT INTO #temp_childOrgs SELECT orgID,orgName,description,parentOrgID,adminID,isActive,@@NESTLEVEL-1 AS level  FROM dbo.ags_orgs WHERE orgID = @orgID

        EXEC getChildOrgs @orgID, @isActive
        -- get next result
        fetch next from cur into @orgID
    END
    CLOSE cur
    DEALLOCATE cur

END

GO

由这个过程调用:

USE [dbname]
GO

/****** Object:  StoredProcedure [dbo].[execGetChildOrgs]    Script Date: 09/26/2012 15:29:34 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE PROCEDURE [dbo].[execGetChildOrgs]

@parentID int,
@isActive tinyint = NULL,
@showParent tinyint = NULL

AS

BEGIN

CREATE TABLE #temp_childOrgs
(
   orgID int,
   orgName varchar(255),
   description text,
   parentOrgID int,
   adminID int,
   isActive tinyint,
   level int
)
-- if this isn't AGS top level (0), make the first record reflect the requested organization
IF @parentID != 0 AND @showParent = 1
BEGIN
    INSERT INTO #temp_childOrgs SELECT orgID,orgName,description,parentOrgID,adminID,isActive,0 AS level  FROM dbo.ags_orgs WHERE orgID = @parentID
END

exec getChildOrgs @parentID, @isActive

SELECT * FROM #temp_childOrgs
DROP TABLE #temp_childOrgs
END

GO
于 2013-10-03T19:10:24.633 回答
0

如果您对层次结构的深度有限制,您可以在一个语句中执行此操作。你说你总共只有4个级别。

SELECT SUM(ProductCount)
FROM (
    SELECT c0.ID, c0.ProductCount
    FROM Categories AS c0
    WHERE c0.ID = 1
    UNION ALL
    SELECT c1.ID, c1.ProductCount
    FROM Categories AS c0
    JOIN Categories AS c1 ON c0.ID = c1.ParentID
    WHERE c0.ID = 1
    UNION ALL
    SELECT c2.ID, c2.ProductCount
    FROM Categories AS c0
    JOIN Categories AS c1 ON c0.ID = c1.ParentID
    JOIN Categories AS c2 ON c1.ID = c2.ParentID
    WHERE c0.ID = 1
    UNION ALL
    SELECT c3.ID, c3.ProductCount
    FROM Categories AS c0
    JOIN Categories AS c1 ON c0.ID = c1.ParentID
    JOIN Categories AS c2 ON c1.ID = c2.ParentID
    JOIN Categories AS c3 ON c2.ID = c3.ParentID
    WHERE c0.ID = 1
) AS _hier;

如果您以您正在做的方式存储层次结构,这将适用于此查询,这称为Adjacency List。基本上,这ParentID是每个节点在层次结构中记录其位置的方式。

还有一些其他存储层次结构的方法可以更轻松地查询整个树或子树。最佳数据组织取决于您要运行的查询。

这里还有一些资源:

于 2013-10-03T23:43:32.113 回答
0

这假设您有名为prods的 Product 表

prod_id|categ_id
        

和名为categ的类别表

categ_id|parent_categ_id

由于您似乎正在使用邻接列表结构,其中外键parent_categ_id列引用同一表中的prod_id列,因此以下查询应该有效


select c1.categ_id,c1.parent_categ_id,count(prods.prod_id)
as product_count from categ c1
join prods on prods.categ_id=c1.categ_id or prods.categ_id
    in( with recursive tree(id,parent_id)as 
    (select categ_id,parent_categ_id from categ 
    where categ_id=c1.categ_id  
    union all
    select cat.categ_id,cat.parent_categ_id from categ cat
    join tree on tree.id=cat.parent_categ_id) select id from tree)
group by c1.categ_id,c1.parent_categ_id 
order by product_count
于 2020-08-01T03:21:52.083 回答
0

这是我计算所有子类别中产品的程序

DELIMITER $$
CREATE PROCEDURE CountItemsInCategories(IN tmpTable INT, IN parentId INT, IN updateId INT)
BEGIN

    DECLARE itemId INT DEFAULT NULL;
    DECLARE countItems INT DEFAULT NULL;
    DECLARE done INT DEFAULT FALSE;
    DECLARE recCount INT DEFAULT NULL;

    DECLARE 
        bufItemCategory CURSOR FOR
    SELECT
        itemCategory.id AS id,
        COUNT(CASE WHEN item.isVisible = 1 then 1 ELSE NULL END) items
    FROM
        itemCategory
    LEFT JOIN item ON
        item.categoryId = itemCategory.id 
    WHERE
        itemCategory.isVisible = 1 AND itemCategory.categoryParentId = parentId 
    GROUP BY
        itemCategory.id
    ORDER BY
        itemCategory.name;

    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

    SET max_sp_recursion_depth = 10000;

    IF tmpTable = 1 THEN
        DROP TEMPORARY TABLE IF EXISTS tblResults;
        CREATE TEMPORARY TABLE IF NOT EXISTS tblResults(
            id INT NOT NULL PRIMARY KEY,
            items INT
        );
    END IF;

    OPEN bufItemCategory;

        Reading_bufItemCategory: LOOP

            FETCH FROM bufItemCategory INTO itemId, countItems;

            IF done THEN
                LEAVE Reading_bufItemCategory;
            END IF;

            IF tmpTable = 1 THEN
                INSERT INTO tblResults VALUES(itemId, countItems);
            ELSE
                UPDATE tblResults SET items = items + countItems WHERE id = updateId;
            END IF;

            SET recCount = (SELECT count(*) FROM itemCategory WHERE itemCategory.categoryParentId = itemId AND itemCategory.isVisible = 1);

            IF recCount > 0 THEN
                CALL CountItemsInCategories(0, itemId, CASE WHEN updateId = 0 then itemId ELSE updateId END);
            END IF;

        END LOOP Reading_bufItemCategory;

    CLOSE bufItemCategory;

    IF tmpTable = 1 THEN
        SELECT * FROM tblResults WHERE items > 0;
        DROP TEMPORARY TABLE IF EXISTS tblResults;
    END IF;

END $$
DELIMITER;

要调用程序,只需运行:

CountItemsInCategories(firstLoop,parentId,updateId);

其中参数是:

firstLoop - 第一个循环始终为“1”

parentId - 子类别的父级

updateId - 要更新的行的 ID,第一个循环始终为“0”

例如:

CountItemsInCategories(1,1,0);

我希望这个例子对某人有用。

于 2019-12-11T12:01:33.220 回答