[英]How do I get rows to correspond to other rows based on a parent_id , all from the same table
[英]How can I recursively obtain the “parent ID” of rows in this MySQL table?
我的數據庫看起來像(pligg cms,樣本數據)
id catID parentID catName
1 1 0 location
2 2 0 color
3 3 1 USA
4 4 3 Illinois
5 5 3 Chicago
6 6 2 Black
7 7 2 Red
假設,如何獲取芝加哥的頂級parentID,它應該是位置。
我是否在php中編寫了遞歸函數,或者在mysql中可行?
該網站非常好地概述了在mysql和PHP中存儲分層數據的不同方法。 要回答您的問題,最簡單的方法是使用php和遞歸。 您還可以使用其他方法,例如modified preorder transversal
,它不需要多個數據庫查詢。 但是,在處理大量插入和更新時,此方法的實現可能會更加復雜。
另一種非常酷的方法,也是我個人最喜歡的方法,是將平面表解析為樹的最有效/最優雅的方法中提到的所謂的“閉合表” /“鄰接關系” 。
關於您的評論,您基本上必須進行循環或遞歸函數,以選擇芝加哥的父母,然后選擇父母的父母,依此類推。
$stack = array();
$parent = 3;
while($parent != 0){
$data = (put your mysql to get the row with parentID = $parent)
$parent = data['parentID'];
$stack[] = $data;
}
$stack = array_reverse($stack);
然后堆棧將包含芝加哥的父母(即美國的位置)
由於mysql尚不支持通過(oracle)連接或通過通用表表達式(sql server)等功能,因此您遇到的大多數示例都需要從php到mysql進行多次調用-在層次結構中每個級別調用一次。 如果您有許多級別的樹並且有大量並發用戶,那么這很快就會加起來,例如,一棵深度為20的樹(具有1000個並發用戶)將需要對數據庫進行20K調用。 以下方法使用存儲過程,並且對於同一場景,僅需要1000個調用(每個用戶1個)。 對結果集的處理取決於您自己:您可以生成XML DOM,將其加載到數組中或僅將其輸出為HTML,重要的一點是,您可以在單個調用中將整個樹包含在結果集中。
編輯:添加了一個category_parent存儲過程,最初已誤讀您的問題。
示例mysql調用
call category_hier(1);
call category_hier(2);
call category_parent(5);
call category_parent(7);
示例php腳本
<?php
$conn = new mysqli("localhost", "foo_dbo", "pass", "foo_db", 3306);
$result = $conn->query(sprintf("call category_parent(%d)", 5));
$row = $result->fetch_assoc();
$result->close();
$conn->close();
echo sprintf("parent category is : cat_id = %s category_name = %s",
$row["cat_id"], $row["category_name"]);
?>
<?php
$conn = new mysqli("localhost", "foo_dbo", "pass", "foo_db", 3306);
$result = $conn->query(sprintf("call category_hier(%d)", 1));
echo "<table border='1'>
<tr><th>cat_id</th><th>category_name</th><th>parent_cat_id</th>
<th>parent_category_name</th><th>depth</th></tr>";
while($row = $result->fetch_assoc()){
echo sprintf("<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>",
$row["cat_id"],$row["category_name"],$row["parent_cat_id"],
$row["parent_category_name"],$row["depth"]);
}
echo "</table>";
$result->close();
$conn->close();
?>
完整的腳本可以在這里找到 -http : //pastie.org/1244582 或參見以下內容:
-- TABLES
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_parent;
delimiter #
create procedure category_parent
(
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;
/* 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 p inner join hier on p.cat_id = hier.parent_cat_id and hier.depth = v_depth) then
insert into hier
select p.parent_cat_id, p.cat_id, v_depth + 1 from categories p
inner join tmp on p.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;
select
c.cat_id,
c.name as category_name
from
hier
inner join categories c on hier.cat_id = c.cat_id
where
hier.parent_cat_id is null;
drop temporary table if exists hier;
drop temporary table if exists tmp;
end #
delimiter ;
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;
/* 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 p inner join hier on p.parent_cat_id = hier.cat_id and hier.depth = v_depth) then
insert into hier
select p.parent_cat_id, p.cat_id, v_depth + 1 from categories p
inner join tmp on p.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
p.cat_id,
p.name as category_name,
b.cat_id as parent_cat_id,
b.name as parent_category_name,
hier.depth
from
hier
inner join categories p on hier.cat_id = p.cat_id
left outer join categories b on hier.parent_cat_id = b.cat_id
order by
hier.depth, hier.cat_id;
drop temporary table if exists hier;
drop temporary table if exists tmp;
end #
delimiter ;
-- TESTING (call this stored procedure from php)
call category_hier(1);
call category_hier(2);
call category_parent(5);
call category_parent(7);
希望這可以幫助。
基於@GWW答案
function getLocationArray($start){
$link=dbConnect();//a function returning the link with your db
$stack = array();
$parent = $start;
while($parent != 0){
$query='SELECT catName,parentID from myTable where catId='.$parent;
$result = mysql_query($query,$link);
while($row = mysql_fetch_assoc($result)){
$parent=$row['parentID'];
$name=$row['catName'];
$stack[] = $name;
/*foreach($row as $cname => $cvalue){
}*/
}
}
$stack = array_reverse($stack);
return $stack;
}
var_dump($stack);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.