表格结构:
CREATE TABLE IF NOT EXISTS `categories` (
`id` smallint(4) unsigned NOT NULL AUTO_INCREMENT,
`parentid` smallint(4) unsigned DEFAULT NULL,
`category` varchar(150) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
KEY `parentid` (`parentid`,`category`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=10 ;
INSERT INTO `categories` (`id`, `parentid`, `category`) VALUES
(1, NULL, 'A'),
(2, NULL, 'B'),
(3, 1, 'A.1'),
(4, 1, 'A.2'),
(5, 1, 'A.3'),
(6, 1, 'A.4'),
(7, 3, 'A.1.1'),
(8, 3, 'A.1.2'),
(9, 3, 'A.1.3');
代码“callcategorycount”函数调用“categorycount”过程,可在select语句中使用:
DELIMITER $$
CREATE FUNCTION callcategorycount(id SMALLINT(4)) RETURNS SMALLINT(4) BEGIN
CALL categorycount(id, @categorycount);
RETURN @categorycount;
END$$
CREATE PROCEDURE categorycount(IN categoryid SMALLINT(4), OUT categorycount SMALLINT(4)) BEGIN
DROP TEMPORARY TABLE IF EXISTS results;
DROP TEMPORARY TABLE IF EXISTS temp1;
DROP TEMPORARY TABLE IF EXISTS temp2;
CREATE TEMPORARY TABLE temp1 AS
SELECT DISTINCT id, parentid FROM categories WHERE parentid = categoryid;
CREATE TEMPORARY TABLE results AS
SELECT id, parentid FROM temp1;
WHILE ( SELECT COUNT(*) FROM temp1 ) DO
CREATE TEMPORARY TABLE temp2 AS
SELECT DISTINCT id, parentid FROM categories WHERE parentid IN (SELECT id FROM temp1);
INSERT INTO results SELECT id, parentid FROM temp2;
DROP TEMPORARY TABLE IF EXISTS temp1;
CREATE TEMPORARY TABLE temp1 AS
SELECT id, parentid FROM temp2;
DROP TEMPORARY TABLE IF EXISTS temp2;
END WHILE;
SELECT COUNT(*) INTO categorycount FROM results;
DROP TEMPORARY TABLE IF EXISTS results;
DROP TEMPORARY TABLE IF EXISTS temp1;
END$$
DELIMITER ;
输出(执行时间:1.7886秒):
SELECT id, parentid, category, callcategorycount(id) AS subcategories FROM categories;
|id|parentid|category|subcategories
|1|NULL|A|7
|2|NULL|B|0
|3|1|A.1|3
|4|1|A.2|0
|5|1|A.3|0
|6|1|A.4|0
|7|3|A.1.1|0
|8|3|A.1.2|0
|9|3|A.1.3|0
如何优化“categorycount”程序?
答案 0 :(得分:0)
尝试此查询
SELECT
T2.*,
IFNULL(c.SubCount,0) as SUBCAOUNT
FROM (
SELECT
@r AS _id,
(SELECT @r := parentid FROM categories WHERE id = _id) AS parentid,
@l := @l + 1 AS lvl
FROM
(SELECT @r := 9, @l := 0) vars,
categories m
WHERE @r <> 0) T1
JOIN categories T2
ON T1._id = T2.id
LEFT JOIN (
SELECT
COUNT(id) as SubCount,
parentid
FROM categories
GROUP BY parentid) as c ON c.parentid = T1._id
ORDER BY T1.lvl DESC;
在这个查询中,我使用9作为参数,因为它是A的最终深度。
输出
ID PARENTID CATEGORY SUBCAOUNT
1 (null) A 4
3 1 A.1 3
9 3 A.1.3 0
编辑: 试试这个查询。
SELECT
T2.*,
SUM(c.SubCount) as SUBCAOUNT
FROM (
SELECT
@r AS _id,
(SELECT @r := parentid FROM categories WHERE id = _id) AS parentid,
@l := @l + 1 AS lvl
FROM
(SELECT @r := 9, @l := 0) vars,
categories m
WHERE @r <> 0) T1
JOIN categories T2
ON T1._id = T2.id
LEFT JOIN (
SELECT
IFNULL(COUNT(id),0) as SubCount,
parentid
FROM categories
GROUP BY parentid) as c ON c.parentid = T1._id
ORDER BY T1.lvl DESC;
输出
ID PARENTID CATEGORY SUBCAOUNT
9 3 A.1.3 7
更多编辑
以下是如何做到这一点
SELECT
c.id,
c.category,
IFNULL(Total,0) as No_Of_Children
FROM categories as c
LEFT JOIN (
SELECT
id,
COUNT(parentid) as Total,
parentid
FROM categories
GROUP BY parentid
) as lc ON lc.parentid = c.id;
输出
ID CATEGORY NO_OF_CHILDREN
1 A 4
2 B 0
3 A.1 3
4 A.2 0
5 A.3 0
6 A.4 0
7 A.1.1 0
8 A.1.2 0
9 A.1.3 0
答案 1 :(得分:0)
您可以执行以下操作,但我建议您仔细检查我的逻辑,因为它很快完成。
将计数字段添加到类别表
-- TABLE
drop table if exists categories;
create table categories
(
cat_id smallint unsigned not null auto_increment,
name varchar(255) not null,
parent_cat_id smallint unsigned null,
child_counter smallint unsigned not null default 0,
primary key categories_pk (cat_id),
key categories_parent_idx (parent_cat_id)
)
engine = innodb;
创建用于插入类别的存储过程
-- SPROCS
drop procedure if exists insert_category;
delimiter #
create procedure insert_category
(
in p_name varchar(255),
in p_parent_cat_id smallint unsigned
)
proc_main:begin
declare v_done tinyint unsigned default 0;
declare v_depth smallint unsigned default 0;
insert into categories (name, parent_cat_id) values (p_name, p_parent_cat_id);
if p_parent_cat_id is null then
leave proc_main;
end if;
-- work out the parents
create temporary table hier(
parent_cat_id smallint unsigned,
cat_id smallint unsigned,
depth smallint unsigned default 0
)engine = memory;
insert into hier select parent_cat_id, cat_id, v_depth from categories where cat_id = p_parent_cat_id;
/* http://dev.mysql.com/doc/refman/5.0/en/temporary-table-problems.html */
create temporary table tmp engine=memory select * from hier;
while not v_done do
if exists( select 1 from categories c
inner join tmp on c.cat_id = tmp.parent_cat_id and tmp.depth = v_depth) then
insert into hier
select c.parent_cat_id, c.cat_id, v_depth + 1 from categories c
inner join tmp on c.cat_id = tmp.parent_cat_id and tmp.depth = v_depth;
set v_depth = v_depth + 1;
truncate table tmp;
insert into tmp select * from hier where depth = v_depth;
else
set v_done = 1;
end if;
end while;
-- update parent counts
update categories c
inner join hier h on c.cat_id = h.cat_id
set
c.child_counter = c.child_counter + 1;
drop temporary table if exists hier;
drop temporary table if exists tmp;
end proc_main #
delimiter ;
测试和结果
truncate table categories;
call insert_category('A',null); -- 1
call insert_category('B',null);
select * from categories;
+--------+------+---------------+---------------+
| cat_id | name | parent_cat_id | child_counter |
+--------+------+---------------+---------------+
| 1 | A | NULL | 0 |
| 2 | B | NULL | 0 |
+--------+------+---------------+---------------+
2 rows in set (0.00 sec)
call insert_category('C',1); -- 3
call insert_category('D',1);
call insert_category('E',1); -- 5
call insert_category('F',1);
select * from categories;
+--------+------+---------------+---------------+
| cat_id | name | parent_cat_id | child_counter |
+--------+------+---------------+---------------+
| 1 | A | NULL | 4 |
| 2 | B | NULL | 0 |
| 3 | C | 1 | 0 |
| 4 | D | 1 | 0 |
| 5 | E | 1 | 0 |
| 6 | F | 1 | 0 |
+--------+------+---------------+---------------+
6 rows in set (0.00 sec)
call insert_category('G',3); -- 7
call insert_category('H',3);
call insert_category('I',3); -- 9
select * from categories;
+--------+------+---------------+---------------+
| cat_id | name | parent_cat_id | child_counter |
+--------+------+---------------+---------------+
| 1 | A | NULL | 7 |
| 2 | B | NULL | 0 |
| 3 | C | 1 | 3 |
| 4 | D | 1 | 0 |
| 5 | E | 1 | 0 |
| 6 | F | 1 | 0 |
| 7 | G | 3 | 0 |
| 8 | H | 3 | 0 |
| 9 | I | 3 | 0 |
+--------+------+---------------+---------------+
9 rows in set (0.00 sec)
其他测试
call insert_category('K',7); -- 10
select * from categories;
+--------+------+---------------+---------------+
| cat_id | name | parent_cat_id | child_counter |
+--------+------+---------------+---------------+
| 1 | A | NULL | 8 |
| 2 | B | NULL | 0 |
| 3 | C | 1 | 4 |
| 4 | D | 1 | 0 |
| 5 | E | 1 | 0 |
| 6 | F | 1 | 0 |
| 7 | G | 3 | 1 |
| 8 | H | 3 | 0 |
| 9 | I | 3 | 0 |
| 10 | K | 7 | 0 |
+--------+------+---------------+---------------+
10 rows in set (0.00 sec)
call insert_category('L',9);
call insert_category('M',10);
select * from categories;
+--------+------+---------------+---------------+
| cat_id | name | parent_cat_id | child_counter |
+--------+------+---------------+---------------+
| 1 | A | NULL | 10 |
| 2 | B | NULL | 0 |
| 3 | C | 1 | 6 |
| 4 | D | 1 | 0 |
| 5 | E | 1 | 0 |
| 6 | F | 1 | 0 |
| 7 | G | 3 | 2 |
| 8 | H | 3 | 0 |
| 9 | I | 3 | 1 |
| 10 | K | 7 | 1 |
| 11 | L | 9 | 0 |
| 12 | M | 10 | 0 |
+--------+------+---------------+---------------+
12 rows in set (0.00 sec)
call insert_category('N',7);
select * from categories;
+--------+------+---------------+---------------+
| cat_id | name | parent_cat_id | child_counter |
+--------+------+---------------+---------------+
| 1 | A | NULL | 11 |
| 2 | B | NULL | 0 |
| 3 | C | 1 | 7 |
| 4 | D | 1 | 0 |
| 5 | E | 1 | 0 |
| 6 | F | 1 | 0 |
| 7 | G | 3 | 3 |
| 8 | H | 3 | 0 |
| 9 | I | 3 | 1 |
| 10 | K | 7 | 1 |
| 11 | L | 9 | 0 |
| 12 | M | 10 | 0 |
| 13 | N | 7 | 0 |
+--------+------+---------------+---------------+
13 rows in set (0.00 sec)
call insert_category('O',5);
select * from categories;
+--------+------+---------------+---------------+
| cat_id | name | parent_cat_id | child_counter |
+--------+------+---------------+---------------+
| 1 | A | NULL | 12 |
| 2 | B | NULL | 0 |
| 3 | C | 1 | 7 |
| 4 | D | 1 | 0 |
| 5 | E | 1 | 1 |
| 6 | F | 1 | 0 |
| 7 | G | 3 | 3 |
| 8 | H | 3 | 0 |
| 9 | I | 3 | 1 |
| 10 | K | 7 | 1 |
| 11 | L | 9 | 0 |
| 12 | M | 10 | 0 |
| 13 | N | 7 | 0 |
| 14 | O | 5 | 0 |
+--------+------+---------------+---------------+
14 rows in set (0.00 sec)
这里有完整的脚本:http://pastie.org/5649953
希望它能为您提供帮助或指向最佳解决方案。