can you please help me write SQL select?
I got table:
REGIONS
where "parent_id" referees to "id". Rows with parent_id set to 0 has no parent and are on the top. I need to select data as tree.
Right know Im using recursive function :)
function SelectData($parent=0, $padding='')
{
$q = mysql_query("SELECT * FROM regions WHERE parent=$parent");
while($row = mysql_fetcharray($q))
{
echo($row[name]);
priklad($row[id], $padding.' ');
}
}
I found out it is possible to do with CTE ( Can I select full hierarchy of parents when id and parent id are in the same table? ), but also found out that MySQL doesn't support CTE.
So, is there any way to use one select instead of recursive function in MySQL ? Thanks :)
You can use a non recursive mysql stored procedure which you can call from your php as follows:
Example calls
$result = $conn->query(sprintf("call category_hier(%d)", $catID));
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 |
| 5 | Chicago | 3 | USA | 2 |
| 4 | Illinois | 3 | USA | 2 |
+--------+---------------+---------------+----------------------+-------+
4 rows in set (0.00 sec)
mysql> call category_hier(2);
+--------+---------------+---------------+----------------------+-------+
| cat_id | category_name | parent_cat_id | parent_category_name | depth |
+--------+---------------+---------------+----------------------+-------+
| 2 | Color | NULL | NULL | 0 |
| 6 | Black | 2 | Color | 1 |
| 7 | Red | 2 | Color | 1 |
+--------+---------------+---------------+----------------------+-------+
3 rows in set (0.00 sec)
Full script and test data below
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;
insert into categories (name, parent_cat_id) values
('Location',null),
('Color',null),
('USA',1),
('Illinois',3),
('Chicago',3),
('Black',2),
('Red',2);
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 tmp on c.parent_cat_id = tmp.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.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 category_hier(1);
call category_hier(2);
Hope this helps :)
step1->
select distinct ID from tbl where PARENT_ID=0
store the value in an array, and then for each of the value
step 2->
select ID from TBL where PARENT_ID = $values_from_array
or perhaps this will work
select ID from TBL where PARENT_ID in (select distinct ID from tbl where PARENT_ID=0) order by PARENT_ID
A part of code from my MyBB plugin. its creating a combobox from hierarcical forums->sub->sub
function generate_video_category_select($name, $selectedId, $parent_id = 0, $depth = 1)
{
global $db;
$box = '';
if($depth == 1)
{
$box .= '<select name="' . $name . '">';
$box .='<option value="0" selected="selected">Boş</option>';
}
$sql = "SELECT * FROM ".TABLE_PREFIX."aofvideo_categories WHERE parent_id = ".$parent_id." ORDER BY name ASC";
$result = $db->query($sql);
while($row = $db->fetch_array($result))
{
$box .='<option value="'.$row['id'].'"'.($selectedId == $row['id'] ? ' selected="selected"' : '').'>'.str_repeat(' ',$depth).$row['name'].'</option>';
$box .= generate_video_category_select($name,$selectedId,$row['id'],$depth + 5);
}
if($depth == 1)
$box .="</select>";
return $box;
}
Whenever you need to extract information from a table that is dependent on other information in the same table it is a good idea to use two 'virtual' tables that refer to the same one. In the case where you know that the relationship is only two deep you could use the following:
select a.name, b.name, from religion a, religion b where a.id=b.parent_id
an example for 3 would be the following:
select a.name, b.name, c.name from religion a, religion b, religion c where a.id=b.parent_id
Hope that helps.