我即将给出100个赏金点来回答这个问题
所以我对递归有一个非常困难的问题 - 如何获得所有项目的类别和包含该父项的所有子项,并且直到最后更深入?
我有桌子:
+----+---------------+-----------------+
| id | category name | category_parent |
+----+---------------+-----------------+
| 1 | cars | 0 |
+----+---------------+-----------------+
| 2 | real estate | 0 |
+----+---------------+-----------------+
| 3 | clothes | 0 |
+----+---------------+-----------------+
| 4 | bmw | 1 |
+----+---------------+-----------------+
| 5 | audi | 1 |
+----+---------------+-----------------+
| 6 | 100 | 5 |
+----+---------------+-----------------+
| 7 | 80 | 5 |
+----+---------------+-----------------+
| 8 | A4 | 5 |
+----+---------------+-----------------+
| 9 | QUATRO | 8 |
+----+---------------+-----------------+
| 10 | TDI | 8 |
+----+---------------+-----------------+
| 11 | Black | 9 |
+----+---------------+-----------------+
| 12 | White | 9 |
+----+---------------+-----------------+
| 13 | 2 doors | 11 |
+----+---------------+-----------------+
| 14 | 5 doors | 11 |
+----+---------------+-----------------+
我的产品表看起来像这样:
+----+---------------+-----------------+
| id | category_id | name |
+----+---------------+-----------------+
例如,我想计算cars
类别中的所有项目。所以基本上我应该传递这个类别id (1)
并以某种方式进行递归来计算所有项目。但是我不知道如何处理它,因为那个类别的孩子可以是无限的。
所以当我想知道那个父母计数的所有项目时,我应该做这样的事情:
1++:
4++
5++:
6++
7++
8++:
9++:
11++:
13++
14++
12++
10++
我希望你能理解我需要什么,并给我任何可以帮助我的建议。
另外,这是我到目前为止所做的开始 - 我可以实现它但是将来我会陷入递归......所以它没有任何价值。
public function get_category_tree_id_list($cat_id, $list_array = FALSE)
{
if ( !$list_array ){
$items = $this->system->_getCustomTableData('categories', array(array('category_parent' => $cat_id)), 'id DESC');
$this->__tmp['id_list'] = [];
foreach ( $items as $key => $value ) {
$this->__tmp['id_list'][] = $value['id'];
}
}
}
答案 0 :(得分:14)
您很可能想要嵌套集。它们设置起来有点棘手,但使查询更简单。因此,您将有两列 - lft
和rgt
,而不是类别父级。左侧和右侧基本上是类别的边界,如果项目的类别ID在这些值之间,您知道它是该类别的子级。
+----+---------------+-----+------+
| id | category name | lft | rgt |
+----+---------------+-----+------+
| 1 | cars | 1 | 24 |
+----+---------------+-----+------+
| 2 | bmw | 2 | 3 |
+----+---------------+-----+------+
| 5 | audi | 4 | 23 |
+----+---------------+-----+------+
| 6 | 100 | 5 | 6 |
+----+---------------+-----+------+
| 7 | 80 | 7 | 8 |
+----+---------------+-----+------+
| 8 | A4 | 9 | 22 |
+----+---------------+-----+------+
| 9 | TDI | 10 | 11 |
+----+---------------+-----+------+
| 10 | Quatro | 12 | 21 |
+----+---------------+-----+------+
| 11 | Black | 13 | 18 |
+----+---------------+-----+------+
| 12 | White | 19 | 20 |
+----+---------------+-----+------+
| 13 | 2 doors | 14 | 15 |
+----+---------------+-----+------+
| 14 | 5 doors | 16 | 17 |
+----+---------------+-----+------+
然后,为了获得汽车类别中的物品数量,你可以像这样简单地完成:
SELECT categories.name, items.id, items.category_id, items.name
FROM categories
LEFT JOIN items
ON (items.category_id BETWEEN categories.lft AND categories.rgt)
WHERE categories.category_name = 'cars'
显然,您只需更改category_name
的值并获取任何类别的项目。
很抱歉,出于某种原因,我在此处上传图片时图像已旋转,但如果您将类别绘制为圆圈,然后对线条进行编号,则可以看到左右值应该是什么。
我只做了汽车,因为我认为你可以推断其他类别。
所以如果你写出这样的类别:
Cars(BMW(), Audi(100(),80(),A4(TDI(),Quatro(Black(2dr(),5dr()), White())))
然后你可以用数字标记括号:
Cars[1]->(BMW[2]->()<-[3], Audi[4]->(100[5]->()<-[6],80[7]->()<-[8],A4[9]->(TDI[10]->()<-[11],Quatro[12]->(Black[13]->(2dr[14]->()<-[15], 5dr[16]->()<-[17])<-[18], White[19]->()<-[20])<-[21])<-[22])<-[23])<-[24]
或者,如果您将其标记为树,则可以将其标记为这样,在此处用最大的数字标记最左边的节点,并在标记所有节点的子节点时仅标记右节点:
答案 1 :(得分:4)
我有一个新想法,我觉得它会很好。 这个想法是这样的: 在category_parent列中,我们将插入对该节点的所有父节点的引用。
+----+---------------+-----------------+ | id | category name | hierarchy | +----+---------------+-----------------+ | 1 | cars | 1 | +----+---------------+-----------------+ | 2 | real estate | 2 | +----+---------------+-----------------+ | 3 | clothes | 3 | +----+---------------+-----------------+ | 4 | bmw | 1-4 | +----+---------------+-----------------+ | 5 | audi | 1-5 | +----+---------------+-----------------+ | 6 | 100 | 1-4-6 | +----+---------------+-----------------+ | 7 | 80 | 1-4-7 | +----+---------------+-----------------+ | 8 | A4 | 1-4-8 | +----+---------------+-----------------+ | 9 | QUATRO | 1-4-8-9 | +----+---------------+-----------------+ | 10 | TDI | 1-4-8-10 | +----+---------------+-----------------+ | 11 | Black | 1-4-8-9-11 | +----+---------------+-----------------+ | 12 | White | 1-4-8-9-12 | +----+---------------+-----------------+ | 13 | 2 doors | 1-4-8-9-11-13 | +----+---------------+-----------------+ | 14 | 5 doors | 1-4-8-9-11-14 | +----+---------------+-----------------+
如果你查看我的更新表,你会发现每条记录都有一个链接到它的父母,不仅是直接的,还有所有的父母。 为了这项工作,我做了一些修改,插入:
Insert into table_name (category_name, hierarchy) values ('new_name', (concat(parent_hierarch, '-', (SELECT Auto_increment FROM information_schema.tables WHERE table_name='table_name'))))
现在让我们进行所需的查询:
1-所有子类别的汽车:
select * from table_name where hierarchy like '1-%'
2-如果你需要BLACK的所有父母,你只需输入:
select * from table_name where hierarchy = '1-4-8-9' or hierarchy = '1-4-8' or hierarchy = '1-4' or hierarchy = '1'
(您可以从php构建该查询,在' - 'char处拆分层次结构字段)
3-查看具有级别和直接父级的所有类别:
select *, SUBSTR(hierarchy, 1, (LENGTH(hierarchy) - LENGTH(id) - 1)) as parent, LENGTH(hierarchy) - LENGTH(REPLACE(hierarchy, '-', '')) as level From table_name
+----+---------------+-----------------+-----------+--------+ | id | category name | hierarchy | parent | level | +----+---------------+-----------------+-----------+--------+ | 1 | cars | 1 | | 0 | +----+---------------+-----------------+-----------+--------+ | 2 | real estate | 2 | | 0 | +----+---------------+-----------------+-----------+--------+ | 3 | clothes | 3 | | 0 | +----+---------------+-----------------+-----------+--------+ | 4 | bmw | 1-4 | 1 | 1 | +----+---------------+-----------------+-----------+--------+ | 5 | audi | 1-5 | 1 | 1 | +----+---------------+-----------------+-----------+--------+ | 6 | 100 | 1-4-6 | 1-4 | 2 | +----+---------------+-----------------+-----------+--------+ | 7 | 80 | 1-4-7 | 1-4 | 2 | +----+---------------+-----------------+-----------+--------+ | 8 | A4 | 1-4-8 | 1-4 | 2 | +----+---------------+-----------------+-----------+--------+ | 9 | QUATRO | 1-4-8-9 | 1-4-8 | 3 | +----+---------------+-----------------+-----------+--------+ | 10 | TDI | 1-4-8-10 | 1-4-8 | 3 | +----+---------------+-----------------+-----------+--------+ | 11 | Black | 1-4-8-9-11 | 1-4-8-9 | 4 | +----+---------------+-----------------+-----------+--------+ | 12 | White | 1-4-8-9-12 | 1-4-8-9 | 4 | +----+---------------+-----------------+-----------+--------+ | 13 | 2 doors | 1-4-8-9-11-13 |1-4-8-9-11 | 5 | +----+---------------+-----------------+-----------+--------+ | 14 | 5 doors | 1-4-8-9-11-14 |1-4-8-9-11 | 5 | +----+---------------+-----------------+-----------+--------+
这是一个新想法,需要一些改进。 我希望你能从中受益。
答案 2 :(得分:3)
您可能希望查看有关在MySQL中处理树结构数据的建议的文章。
http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql
您选择采用的方法可能取决于您的应用程序读写用例包含的内容。
目前,您正在使用邻接列表模型,这在使用任意树深度类型的查询时尤其成问题。如果这是您的主要用例,您可能需要考虑嵌套集方法,虽然可能不那么直观,但更适合任意深度树查询。
嵌套集方法的挑战是对表的更新通常更难以管理。
答案 3 :(得分:1)
要调用它,只需将对象targetId构造为参数:
喜欢:
$counter = new RecursiveCounter(8);
$count = $counter->getCount();
班级:
class RecursiveCounter {
private $row;
private $targetId;
public function __construct($targetId) {
//Just setting up the info I need. You need to consider how to get the data from database and replace the constructor
$this->row = array(
1 => array("category" => "cars", "parent" => 0),
2 => array("category" => "realestate", "parent" => 0),
3 => array("category" => "clothes", "parent" => 0),
4 => array("category" => "bmw", "parent" => 1),
5 => array("category" => "audi", "parent" => 1),
6 => array("category" => "100", "parent" => 5),
7 => array("category" => "80", "parent" => 5),
8 => array("category" => "A4", "parent" => 5),
9 => array("category" => "QUATRO", "parent" => 8),
10 => array("category" => "TDI", "parent" => 8),
11 => array("category" => "Black", "parent" => 9),
12 => array("category" => "White", "parent" => 9),
13 => array("category" => "doors", "parent" => 11),
14 => array("category" => "doors", "parent" => 11)
);
$this->targetId = $targetId;
}
public function getCount() {
// Entry point
$count = 0;
foreach ($this->row as $id => $row) {
if ($this->isMatchTarget($id)) {
$count++;
}
}
return $count;
}
private function getParent($id) {
$parentId = $this->row[$id]["parent"];
if (array_key_exists($parentId, $this->row)) {
return $parentId;
} else {
return false;
}
}
private function isMatchTarget($id) {
// 1. If the supplied id is the target id, job is done and return true;
// 2. If not:
// Get the parent ud;
// If parent id is not 0 (Meaning it has a parent), keep on checking
// What to check? Check if the parent id is matching
// If the the parent id is still not equal to target or 0, it will check the parent of parent until it they are equal or it is 0
// if there is no parent, and the id dont match (Ending condidtion)
// return false;
if ($id == $this->targetId) {
return true;
} else {
$parentId = $this->getParent($id);
if (0 != $parentId) {
return $this->isMatchTarget($parentId);
} else {
return false;
}
}
}
}
答案 4 :(得分:1)
您可以在php中使用递归函数来获取类别及其子项中的产品数量
public function get_number_of_products_in_category($cat_id)
{
$qty = 0;
//get number of product in this category
$nb_products = $this->system->_getRowsCount('products', array(array('category_id' => $cat_id)));
$qty += $nb_products;
//get all child categories
$items = $this->system->_getCustomTableData('categories', array(array('category_parent' => $cat_id)), 'id DESC');
//add number of products in the child category
if(!empty($items)) {
foreach ( $items as $key => $value ) {
$qty += get_number_of_products_in_category($value['id']);
}
}
return $qty;
}
要加快查询速度,您应该为这些列设置关键字:
答案 5 :(得分:1)
如果正确使用索引,在MySQL中使用索引几乎不使用任何资源。因此,您可以计算每个类别的产品数量,然后使用PHP来完成这项工作。
$query="SELECT c.*, count(p.*) total
FROM category, product p on c.id=p.category_id
GROUP BY c.id;";
假设您将上一个查询的结果存储在$result_categories
数组中
function getMatchingItemsNb($result_categories, $category_id) {
$sum=0;
foreach ($result_categories as $row) {
if ($row['id']==$category_id) {
$sum+=$row['total'];
} else if ($row['category_parent']==$category_id) {
$sum+=getMatchingItemsNb($result_categories, $row['category_parent']);
}
}
return $sum;
}
这只需要1个SQL请求,这将有利于索引和缓存。它可用于获取多个类别,而无需两次查询mysql。
为了获得更好的性能,您还可以考虑在total
表中添加category
字段,即使它意味着一些冗余,但我认为修改数据结构不是此处的主题。 / p>
答案 6 :(得分:0)
基本上无论数据库表中的数据是什么,我们都必须首先通过java将其加载到内存中,而不是下面的magic begin,希望它找到好
ORA-08103: Object No Longer Exists