召集所有mySQL专家!
我需要对mySQL进行复杂的查询,但我无法理解它。有2个表格有问题:
地点
(列:location_id,父级,位置)
数据以分层方式分为国家,地区,县和城镇,因此:
1,0,英格兰( country )
2,1,西南(地区)
3,1,东南(地区)
4,2,多塞特(县)
5,4,伯恩茅斯( town )
6,4,普尔( town )
7,4,Wimborne( town )
等多达400多行的位置数据
简档
(列:profile_id,title,location_id)
每行都有一个位置ID,它始终是一个城镇(即最后一个孩子)。例如:
1,'此配置文件的位置设置为伯恩茅斯',5
2,'此配置文件的位置设置为普尔',6
等
我需要实现的是从Locations表中返回所有ID,其中或其子项具有与之关联的条目。因此,在上面的示例中,我需要返回以下位置ID:1,2,4,5,6
原因:
1 - 是的,英格兰是西南,多塞特郡和伯恩茅斯的母公司,其中有一个条目
2 - 是的,西南是多塞特和伯恩茅斯的母公司,其中有一个条目
3 - 否,东南没有任何条目或其任何子女
4 - 是的,多塞特是伯恩茅斯的父母,其中有一个参赛作品
5 - 是的,伯恩茅斯有一个条目
6 - 是的,普尔有一个条目
7 - 不,Wimborne没有参赛作品
那么,这实际上是可能的吗?我试图在PHP中使用嵌套的SQL查询,但脚本超时,所以必须有一种方法只在SQL查询中执行此操作?
提前感谢你! :)
===========
更新
在阅读并播放所有这些解决方案后,我意识到我完全以错误的方式解决问题。不是遍历所有位置并返回那些具有条目的位置,而是更有意义,并且更有效地获取所有条目并返回相应的位置,然后上层级以使每个位置成为父级,直到根被击中。 / p>
非常感谢你的帮助,至少让我意识到我的尝试是不必要的。
答案 0 :(得分:0)
事实上,你的脚本超时会在某处显示无限循环。
考虑到你是根据子区域引用位置表,再加上对父区域的另一个引用,你需要使用PHP和& amp;组合。 Mysql滚动浏览所有这些 - 一个简单的JOIN语句在这种情况下不起作用,我不认为。
此外,您需要更改表格,以便如果它是顶级页面,则其parent_id为NULL,而不是0.完成后...
$sql = "SELECT * FROM locations WHERE parent =''";
$result = mysql_query($sql);
while($country = mysql_fetch_array($result)) {
$subsql = "SELECT * FROM locations WHERE parent='".$country['id']."'";
$subresult = mysql_query($subsql);
while($subregion = mysql_fetch_array($subresult)) {
$profilesql = "SELECT * FROM profiles WHERE location_id='".$subregion['id']."'";
$profileresult = mysql_query($profilesql);
echo mysql_num_rows($profileresult).' rows under '.$subregion['location'].'.<br />';
}
}
基本代码在那里......有没有人有一个聪明的想法让它适用于各种子级别?但老实说,如果这是我的项目,我会为Country,然后是Regions,然后是City / Town制作单独的表。 3个表将使数据导航更容易。
答案 1 :(得分:0)
如果你的php代码很好,你可能在[location - &gt;中有一个嵌套循环。父母] fd。我会先从那里开始,然后使用PHP。我认为SQL没有递归函数。
如果你需要一个嵌套的父循环,你应该写一个合并算法的变异来解决这个问题。
在PHP中找到嵌套循环
$ids = array();
function nestedLoopFinder($parent)
{
global $ids;
$result = mysql_query("SELECT location_id FROM locations WHERE parent=$parent");
while($row = mysql_fetch_object($result))
{
if(in_array($row->location_id, $ids)) {
die("duplicate found: $row->location_id");
}
$ids[] = $row->location_id;
//recurse
nestedLoopFinder($row->location_id);
}
}
答案 2 :(得分:0)
我处理这个问题的方法是只进行一次SQL加载,然后将引用放在父对象中。
$locations = array(); $obj_query = "SELECT * from locations"; $result_resource = mysql_query($obj_query); while ($row = mysql_fetch_assoc($result_resource) { $locations[$row['location_id'] = (object) $row; } foreach ($locations as $location) { if (isset($location->parent) { $locations[$location->parent]->children[] = $location; } }
您的对象需要一个这样的方法来确定某个位置是否是后代:
function IsAnscestorOF ($location) { if (empty($children)) { return false; } if (in_array($location, keys($this->children) { return true; } else { foreach ($children as $child) { if ($child->isAnscestor) { return true; } } } return false; }
答案 3 :(得分:0)
不确定我是否完全理解您的要求,但以下存储过程示例可能是您的良好起点:
示例调用(请注意包含的列)
mysql> call location_hier(1);
+-------------+---------------------+--------------------+---------------------+-------+----------+
| location_id | location | parent_location_id | parent_location | depth | included |
+-------------+---------------------+--------------------+---------------------+-------+----------+
| 1 | England (country) | NULL | NULL | 0 | 1 |
| 2 | South West (region) | 1 | England (country) | 1 | 1 |
| 3 | South East (region) | 1 | England (country) | 1 | 0 |
| 4 | Dorset (county) | 2 | South West (region) | 2 | 1 |
| 5 | Bournemouth (town) | 4 | Dorset (county) | 3 | 1 |
| 6 | Poole (town) | 4 | Dorset (county) | 3 | 1 |
| 7 | Wimborne (town) | 4 | Dorset (county) | 3 | 0 |
+-------------+---------------------+--------------------+---------------------+-------+----------+
7 rows in set (0.00 sec)
您将从php调用存储过程,如下所示:
$startLocationID = 1;
$result = $conn->query(sprintf("call location_hier(%d)", $startLocationID));
完整脚本:
drop table if exists profiles;
create table profiles
(
profile_id smallint unsigned not null auto_increment primary key,
location_id smallint unsigned null,
key (location_id)
)
engine = innodb;
insert into profiles (location_id) values (5),(6);
drop table if exists locations;
create table locations
(
location_id smallint unsigned not null auto_increment primary key,
location varchar(255) not null,
parent_location_id smallint unsigned null,
key (parent_location_id)
)
engine = innodb;
insert into locations (location, parent_location_id) values
('England (country)',null),
('South West (region)',1),
('South East (region)',1),
('Dorset (county)',2),
('Bournemouth (town)',4),
('Poole (town)',4),
('Wimborne (town)',4);
drop procedure if exists location_hier;
delimiter #
create procedure location_hier
(
in p_location_id smallint unsigned
)
begin
declare v_done tinyint unsigned default 0;
declare v_depth smallint unsigned default 0;
create temporary table hier(
parent_location_id smallint unsigned,
location_id smallint unsigned,
depth smallint unsigned default 0,
included tinyint unsigned default 0,
primary key (location_id),
key (parent_location_id)
)engine = memory;
insert into hier select parent_location_id, location_id, v_depth, 0 from locations where location_id = p_location_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 locations c
inner join tmp on c.parent_location_id = tmp.location_id and tmp.depth = v_depth) then
insert into hier select c.parent_location_id, c.location_id, v_depth + 1, 0 from locations c
inner join tmp on c.parent_location_id = tmp.location_id and tmp.depth = v_depth;
update hier inner join tmp on hier.location_id = tmp.parent_location_id
set hier.included = 1;
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;
update hier inner join tmp on hier.location_id = tmp.parent_location_id
set hier.included = 1;
-- include any locations that have profiles ???
update hier inner join profiles on hier.location_id = profiles.location_id
set hier.included = 1;
-- output the results
select
c.location_id,
c.location as location,
p.location_id as parent_location_id,
p.location as parent_location,
hier.depth,
hier.included
from
hier
inner join locations c on hier.location_id = c.location_id
left outer join locations p on hier.parent_location_id = p.location_id
-- where included = 1 -- filter in your php or here up to you !
order by
hier.depth;
-- clean up
drop temporary table if exists hier;
drop temporary table if exists tmp;
end #
delimiter ;
call location_hier(1);
希望这会有所帮助:)