1

テーブル構造:

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」関数は、select ステートメントで使用できるように「categorycount」プロシージャを呼び出します。

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」手順を最適化するには?

4

2 に答える 2

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

それが最適な解決策に役立つか、あなたを指し示してくれることを願っています。

于 2013-01-08T19:38:24.703 に答える