我有两张桌子;类别和产品。对于每个类别,我想计算其所有子类别中有多少产品。我已经计算了每个类别中有多少。示例表是:
分类
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
答案 0 :(得分: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
答案 1 :(得分: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;
如果以您正在进行的方式存储层次结构,那么这将适用于此查询,称为邻接列表。基本上,ParentID
是每个节点记录其在层次结构中的位置的方式。
还有一些其他存储层次结构的方法可以更容易地查询整个树或子树。最佳数据组织取决于您要运行的查询。
以下是更多资源:
答案 2 :(得分: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);
我希望这个例子对某人有用。
答案 3 :(得分:0)
这假设您有 产品表名为 prods
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