Php 制作无限子类别的最佳方法是什么

Php 制作无限子类别的最佳方法是什么,php,mysql,Php,Mysql,我已经了解了这个方法,但是这个方法不是很好,因为它循环sql查询数百万次 有人知道使用相同MySQL结构的其他方法吗 CREATE TABLE IF NOT EXISTS `jooria_categories` ( `id` int(11) NOT NULL AUTO_INCREMENT, `title` text COLLATE utf8_bin NOT NULL, `sub` int(11) NOT NULL DEFAULT '0', PRIMARY KEY (`id`) )

我已经了解了这个方法,但是这个方法不是很好,因为它循环sql查询数百万次

有人知道使用相同MySQL结构的其他方法吗

CREATE TABLE IF NOT EXISTS `jooria_categories` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` text COLLATE utf8_bin NOT NULL,
  `sub` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=1;

我相信这更像是一个数据库设计问题,而不是其他任何问题。你问的是如何创建“无限子类别”,但如果我理解正确的话,你想要的是一个正常的树结构(父、子、孙)


对此建模的最简单方法是有三列:
id
title
、和
parent
parent
是引用当前表的“外键”。如果
parent
设置为null,则该类别为“顶级”;如果未设置(并设置为表中的现有记录),则该类别为子类别。

我认为,这更像是一个数据库设计问题。你问的是如何创建“无限子类别”,但如果我理解正确的话,你想要的是一个正常的树结构(父、子、孙)


对此建模的最简单方法是有三列:
id
title
、和
parent
parent
是引用当前表的“外键”。如果
parent
设置为null,则类别为“顶级”;如果未设置(并设置为表中的现有记录),则为子类别。

未阅读文章,但我查看了您的sql。一般来说,这样做的一个好模式是跟踪项目的父类而不是子类别。我认为这可以避免一些不必要的迭代


它的想法与。

没有读过这篇文章,但我查看了您的sql。一般来说,这样做的一个好模式是跟踪项目的父类而不是子类别。我认为这可以避免一些不必要的迭代


这与。

的想法相同。您可以使用此结构并创建一个附加列来存储所有子类别。有了这个专栏,我能想到的大多数任务只需要一个查询

例如:

a
    b
        d
    c
该树将表示为:

id  title   parent  parents
1   a       0       0
2   b       1       0,1
3   c       1       0,1
4   d       2       0,1,2

可以使用此结构并创建一个附加列来存储所有子类别。有了这个专栏,我能想到的大多数任务只需要一个查询

例如:

a
    b
        d
    c
该树将表示为:

id  title   parent  parents
1   a       0       0
2   b       1       0,1
3   c       1       0,1
4   d       2       0,1,2

为此,有四种广泛使用的方法:

  • 邻接表的定义
邻接列表(您文章中的那一个)是最方便的,但是它需要对递归查询的支持,而
MySQL
缺乏这种支持,尽管在某种程度上可以对其进行模拟:


有四种广泛使用的方法:

  • 邻接表的定义
邻接列表(您文章中的那一个)是最方便的,但是它需要对递归查询的支持,而
MySQL
缺乏这种支持,尽管在某种程度上可以对其进行模拟:


    • 有几种可能性。您可以存储对行中每个子类别的父级的引用,就像syroin建议的那样。您还可以使用修改的前序树遍历。它更加复杂,但许多树操作在SQL中变得更加简单。作为第三种选择,一些系统(例如CakePHP的TreeBehaviour)将两者结合起来。它们为MPTT存储
      字段,并存储
      字段以便于重新组织


      看看

      有几种可能性。您可以存储对行中每个子类别的父级的引用,就像syroin建议的那样。您还可以使用修改的前序树遍历。它更加复杂,但许多树操作在SQL中变得更加简单。作为第三种选择,一些系统(例如CakePHP的TreeBehaviour)将两者结合起来。它们为MPTT存储
      字段,并存储
      字段以便于重新组织

      看看




      如果使用存储过程,您可以坚持使用邻接列表实现,只需从应用层(php)调用一次mysql即可。以下存储过程是迭代的,而不是递归的,但在大多数情况下仍能提供良好的性能,邻接列表实现与其他方法相比有许多优点,尤其是在维护层次结构时:

      您可以从php调用存储过程,如下所示:

      $result = $conn->query(sprintf("call category_hier(%d)", 1));
      
      mysql> call category_hier(1);
      +--------+---------------+---------------+----------------------+-------+
      | cat_id | category_name | parent_cat_id | parent_category_name | depth |
      +--------+---------------+---------------+----------------------+-------+
      |      1 | Location      |          NULL | NULL                 |     0 |
      |      3 | USA           |             1 | Location             |     1 |
      |      4 | Illinois      |             3 | USA                  |     2 |
      |      5 | Chicago       |             3 | USA                  |     2 |
      +--------+---------------+---------------+----------------------+-------+
      4 rows in set (0.00 sec)
      
      脚本

      drop table if exists categories;
      create table categories
      (
      cat_id smallint unsigned not null auto_increment primary key,
      name varchar(255) not null,
      parent_cat_id smallint unsigned null,
      key (parent_cat_id)
      )
      engine = innodb;
      
      -- TEST DATA
      
      insert into categories (name, parent_cat_id) values
      ('Location',null), 
      ('Color',null), 
         ('USA',1), 
            ('Illinois',3), 
            ('Chicago',3), 
         ('Black',2), 
         ('Red',2);
      
      -- STORED PROCEDURES
      
      drop procedure if exists category_hier;
      
      delimiter #
      
      create procedure category_hier
      (
      in p_cat_id smallint unsigned
      )
      begin
      
      declare v_done tinyint unsigned default 0;
      declare v_depth smallint unsigned default 0;
      
      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_cat_id;
      create temporary table tmp engine=memory select * from hier;
      
      /* http://dev.mysql.com/doc/refman/5.0/en/temporary-table-problems.html */
      
      while not v_done do
      
          if exists( select 1 from categories c
              inner join hier on c.parent_cat_id = hier.cat_id and hier.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.parent_cat_id = tmp.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;
      
      select 
       c.cat_id,
       c.name as category_name,
       p.cat_id as parent_cat_id,
       p.name as parent_category_name,
       hier.depth
      from 
       hier
      inner join categories c on hier.cat_id = c.cat_id
      left outer join categories p on hier.parent_cat_id = p.cat_id
      order by
       hier.depth;
      
      drop temporary table if exists hier;
      drop temporary table if exists tmp;
      
      end #
      
      delimiter ;
      
      -- call from php
      
      call category_hier(1);
      call category_hier(2);
      

      如果使用存储过程,您可以坚持使用邻接列表实现,只需从应用层(php)调用一次mysql即可。以下存储过程是迭代的,而不是递归的,但在大多数情况下仍能提供良好的性能,邻接列表实现与其他方法相比有许多优点,尤其是在维护层次结构时:

      您可以从php调用存储过程,如下所示:

      $result = $conn->query(sprintf("call category_hier(%d)", 1));
      
      mysql> call category_hier(1);
      +--------+---------------+---------------+----------------------+-------+
      | cat_id | category_name | parent_cat_id | parent_category_name | depth |
      +--------+---------------+---------------+----------------------+-------+
      |      1 | Location      |          NULL | NULL                 |     0 |
      |      3 | USA           |             1 | Location             |     1 |
      |      4 | Illinois      |             3 | USA                  |     2 |
      |      5 | Chicago       |             3 | USA                  |     2 |
      +--------+---------------+---------------+----------------------+-------+
      4 rows in set (0.00 sec)
      
      脚本

      drop table if exists categories;
      create table categories
      (
      cat_id smallint unsigned not null auto_increment primary key,
      name varchar(255) not null,
      parent_cat_id smallint unsigned null,
      key (parent_cat_id)
      )
      engine = innodb;
      
      -- TEST DATA
      
      insert into categories (name, parent_cat_id) values
      ('Location',null), 
      ('Color',null), 
         ('USA',1), 
            ('Illinois',3), 
            ('Chicago',3), 
         ('Black',2), 
         ('Red',2);
      
      -- STORED PROCEDURES
      
      drop procedure if exists category_hier;
      
      delimiter #
      
      create procedure category_hier
      (
      in p_cat_id smallint unsigned
      )
      begin
      
      declare v_done tinyint unsigned default 0;
      declare v_depth smallint unsigned default 0;
      
      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_cat_id;
      create temporary table tmp engine=memory select * from hier;
      
      /* http://dev.mysql.com/doc/refman/5.0/en/temporary-table-problems.html */
      
      while not v_done do
      
          if exists( select 1 from categories c
              inner join hier on c.parent_cat_id = hier.cat_id and hier.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.parent_cat_id = tmp.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;
      
      select 
       c.cat_id,
       c.name as category_name,
       p.cat_id as parent_cat_id,
       p.name as parent_category_name,
       hier.depth
      from 
       hier
      inner join categories c on hier.cat_id = c.cat_id
      left outer join categories p on hier.parent_cat_id = p.cat_id
      order by
       hier.depth;
      
      drop temporary table if exists hier;
      drop temporary table if exists tmp;
      
      end #
      
      delimiter ;
      
      -- call from php
      
      call category_hier(1);
      call category_hier(2);
      

      在提出新问题之前,请检查您过去的答案;成为SO社区的好成员谢谢在提出新问题之前,请检查您过去的答案;成为SO社区的好成员谢谢