我在父数据库和子项数据库的MySQL数据库中有一个类别系统。数据库仅存储它的直接父级的id(如果是root,则为0)。由于系统允许多个子类别,因此存在多个子类的情况。
例如
[98] Storage
[1] External
[3] Pendrives
[4] Portable hhdds
[2] Internal
[5] Sata hhdd
[6] IDE hhdd
[...]
[99] Clothing
数据库将是
id parent_id name
1 98 External
2 98 Internal
3 1 Pendrives
4 1 Portable
5 2 Sata
6 2 IDE
98 0 Storage
99 0 Clothing
我还有一个带有类别ID的产品表,我需要获得第一级别类别中所有产品的列表。
例如:
Product Category
A 3
B 4
C 5
D 6
E 74
应该回来 98:A,B,C,D 99:X,Y,Z ......
我陷入困境,我无法想到以这种方式检索它的逻辑。
我首先通过以下方式获取不在第一级别的所有类别的ID:
while ($row = mysql_fetch_assoc($result)) {
if ($row['parent_id'] != 0) {
$level1[$i]['name'] = utf8_encode($row['categories_name']);
$level1[$i]['id'] = $row['categories_id'];
}
$i++;
}
但是我有一个倦怠,想不出一种可以嵌套它们的方法。我想了一段时间,但它是无限的:P
有什么想法吗?
答案 0 :(得分:1)
使用递归函数获取所有子类别:
//fetch category structure data
$childCategories = array();
$result = mysql_query("SELECT id, parent_id FROM categories");
while ($row = mysql_fetch_assoc($result)) {
if ($row['parent_id']) {
if (!isset($childCategories[$row['parent_id']])) $childCategories[$row['parent_id']] = array();
$childCategories[$row['parent_id']][] = $row['id'];
}
}
function getRecursiveCategories($id, $childCategories) {
$ret = array();
if (!isset($childCategories[$id])) return $ret;
foreach ($childCategories[$id] as $childId) {
$ret[] = $childId;
$ret = array_merge($ret, getRecursiveCategories($childId, $childCategories));
}
return $ret;
}
$cateoryIds = getRecursiveCategories(98, $childCategories);
//now you can query the products
"SELECT * FROM products WHERE category_id IN (".implode(',', $cateoryIds).")";
答案 1 :(得分:1)
递归函数调用怎么样?例如:
$allItems = Array();
function getAllItems($category) {
global $allItems;
$query = mysql_query("SELECT `id` FROM `categories` WHERE `parent_id` = '".$category."' ");
if(mysql_num_rows($query)) {
while($cat = mysql_fetch_assoc($query)) {
$itemQuery = mysql_query("SELECT * FROM `items` WHERE `category` = '".$cat['id']."' ");
if(mysql_num_rows($itemQuery)) {
while($item = mysql_fetch_assoc($itemQuery)) {
$allItems[$item['id']] = $item; //using $item['id'] to avoid duplicating items if they are in several categories
}
}
$checkSubCatQuery = "SELECT `id` FROM `categories` WHERE `parent_id` = '".$cat['id']."' LIMIT 1");
if(mysql_num_rows($checkSubCatQuery)) { //Checking to see if we have a sub category
getAllItems($cat['id']); //Calling agait to get all items from sub cattegory of the current category
}
}
}
}
当然,它总能被优化。
答案 2 :(得分:0)
我知道一个完美的article关于如何正确地做到这一点并且在mysql中具有良好的性能(我不能相信这个想法,我只是从捷克语中翻译)
你需要这样的树结构:
categories (
id,
lft, -- index on "left iteration" - number on the left
rgt, -- index on "right iteration" - number on the right
depth,
)
$result = mysql_query("SELECT * FROM categories ORDER BY lft");
while ($row = mysql_fetch_assoc($result)) {
echo str_repeat("- ", $row["depth"]) . htmlspecialchars($row["data"]) . "<br />";
}
mysql_free_result($result);
如果您需要打印整棵树,则可以按lft
对其进行排序并将其写下来。如果您只需要显示树的一部分,则可以将lft
和rft
限制为大于当前节点值。
$result = mysql_query("SELECT * FROM categories ORDER BY lft");
$depth = -1;
while ($row = mysql_fetch_assoc($result)) {
if ($depth < $row["depth"]) {
echo "<ul>";
} else {
echo str_repeat("</li></ul>", $depth - $row["depth"]) . "</li>";
}
echo "<li>\n" . htmlspecialchars($row["data"]);
$depth = $row["depth"];
}
echo str_repeat("</li></ul>", $depth + 1) . "\n";
mysql_free_result($result);
在有序列表中显示(具有多个级别)时,您必须检测当前节点depth
与下一个项目之间的连接。
$row = mysql_fetch_assoc(mysql_query("SELECT * FROM categories WHERE id = " . intval($_GET["id"])));
$result1 = mysql_query("SELECT * FROM categories WHERE lft < $row[lft] AND rgt > $row[rgt] ORDER BY lft");
while ($row1 = mysql_fetch_assoc($result1)) {
echo "<a href='?id=$row1[id]'>" . htmlspecialchars($row1["data"]) . "</a> > ";
}
mysql_free_result($result1);
echo htmlspecialchars($row["data"]);
mysql_query("INSERT INTO categories (lft, rgt, depth, data)
SELECT IFNULL(MAX(rgt), 0) + 1, IFNULL(MAX(rgt), 0) + 2, 0, '" .
mysql_real_escape_string($_POST["data"]) . "' FROM categories");
mysql_query("START TRANSACTION");
$row = mysql_fetch_assoc(mysql_query("SELECT * FROM categories WHERE id = " . intval($_GET["rodic"]) . " FOR UPDATE"));
mysql_query("UPDATE categories SET lft = lft + 2 WHERE lft > $row[rgt]");
mysql_query("UPDATE categories SET rgt = rgt + 2 WHERE rgt >= $row[rgt]");
mysql_query("INSERT INTO categories (lft, rgt, depth, data) VALUES ($row[rgt], $row[rgt]+1, $row[depth]+1, '" . mysql_real_escape_string($_POST["data"]) . "')");
mysql_query("COMMIT");
mysql_query("START TRANSACTION");
$row = mysql_fetch_assoc(mysql_query("SELECT * FROM categories WHERE id = " . intval($_GET["id"]) . " FOR UPDATE"));
mysql_query("DELETE FROM categories WHERE lft >= $row[lft] AND rgt <= $row[rgt]");
$rozdil = $row["rgt"] - $row["lft"] + 1;
mysql_query("UPDATE categories SET lft = lft - $rozdil WHERE lft > $row[rgt]");
mysql_query("UPDATE categories SET rgt = rgt - $rozdil WHERE rgt > $row[rgt]");
mysql_query("COMMIT");
如果您知道此算法的更多信息(或名称),请告诉我
我建议使用PDO写这个。
答案 3 :(得分:0)
按照以下步骤进行操作(可以提高算法的效率):
首先根据初始类别ID进行选择。像SELECT id, parent_id FROM categories
之类的东西将结果存储在如下数组中:
$catArr = array(
array(1,98),
array(2,98),
...
);
针对以下函数运行此操作(注意:$ start = 98)
function getSubsets($catArr, $start) {
$rtn = array();
foreach($catArr as $cat) {
if($cat[1] == $start) {
$rtn[] = $cat[0];
$subcats = getSubsets($catArr,$cat[0]);
foreach($subcats as $subcat) { $rtn[] = $subcat; }
}
}
return $rtn;
}
note: never have a category be its own parent or any other similar
conditions that would cause an infinite loop.
现在可以像这样生成最终的SQL
$sql = "SELECT * FROM products WHERE category IN (".implode(',', $rtn).")";
Note: this assumes category is a number
最后注意:您只运行两个SQL查询:)