使用 SQL 和 PHP 从不同的引用表中获取分层树
Get a hierarchical tree from different referenced tables with SQL and PHP
我有多个 table 都具有相似的基本结构:
biopsy_p0
id | biopsy_id | introduced
biopsy_p1
id | biopsy_p0_id | introduced
biopsy_p2
id | biopsy_p1_id | introduced
我的目标是获得
之间依赖关系的树视图
biopsy_p0.id->biopsy_p1.biopsy_p0_id->biopsy_p2.biopsy_p1_id
我试着只用 SQL 来做,但是正如你从我的问题中看到的那样,我在这方面不是很有经验。到目前为止我能找到的所有内容都是关于分层树的参考资料。但是那些总是只使用一个 table 和内部引用。
---更新:
我现在让它与 PHP 一起工作,这确实不是一个好的解决方案,我希望我可以用 SQL 来实现它,这样扩展起来会更好一些:
PHP代码:
$database = DatabaseFactory::getFactory()->getConnection();
// Get all p0 element asociated with the biopsy
$sql = "SELECT *
FROM biopsy_p0
WHERE biopsy_id = :id";
$query = $database->prepare($sql);
$query->execute(array(':id' => $id));
$p0 = $query->fetchAll();
// Get all p1 elements
$sql="SELECT *
FROM biopsy_p0 as p0
RIGHT JOIN biopsy_p1 as p1
ON p0.id=p1.biopsy_p0_id
WHERE biopsy_id = :id;";
$query = $database->prepare($sql);
$query->execute(array(':id' => $id));
$p1 = $query->fetchAll();
for ($i=0; $i < count($p0); $i++)
{
$p1Array = new ArrayObject();
foreach ($p1 as $key => $value)
{
if ($value->biopsy_p0_id == $p0[$i]->id)
{
$p1Array->append($value);
}
$p0[$i]->p1 = $p1Array;
}
unset($p1Array);
}
if ($p0 != NULL){
return $p0;
}
return FALSE;
结果:
这正是我需要的,但是 PHP 很乱,而且它的复杂性随着我想检查的每个子级别而增加。
details: Array
(
[0] => stdClass Object
(
[id] => 1
[biopsy_id] => 226
[introduced] => 2014-12-31
[p1] => ArrayObject Object
(
[storage:ArrayObject:private] => Array
(
[0] => stdClass Object
(
[id] => 1
[biopsy_id] => 226
[introduced] => 2015-03-18
[biopsy_p0_id] => 1
)
[1] => stdClass Object
(
[id] => 3
[biopsy_id] => 226
[introduced] => 2015-03-17
[biopsy_p0_id] => 1
)
[2] => stdClass Object
(
[id] => 4
[biopsy_id] => 226
[introduced] => 2015-03-18
[biopsy_p0_id] => 1
)
)
)
)
[1] => stdClass Object
(
[id] => 2
[biopsy_id] => 226
[introduced] => 2014-12-31
[p1] => ArrayObject Object
(
[storage:ArrayObject:private] => Array
(
[0] => stdClass Object
(
[id] => 2
[biopsy_id] => 226
[introduced] => 2015-03-31
[biopsy_p0_id] => 2
)
[1] => stdClass Object
(
[id] => 6
[biopsy_id] => 226
[introduced] => 2015-03-01
[biopsy_p0_id] => 2
)
)
)
)
[2] => stdClass Object
(
[id] => 3
[biopsy_id] => 226
[introduced] => 2014-12-31
[p1] => ArrayObject Object
(
[storage:ArrayObject:private] => Array
(
[0] => stdClass Object
(
[id] => 5
[biopsy_id] => 226
[introduced] => 2015-03-11
[biopsy_p0_id] => 3
)
)
)
)
)
SQL数据:
CREATE TABLE IF NOT EXISTS `biopsy` (
`id` int(11) unsigned NOT NULL,
`creation_date` date NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=228 DEFAULT CHARSET=latin1;
INSERT INTO `biopsy` (`id`, `creation_date`) VALUES
(226, '2015-03-08'),
(227, '2015-03-08');
CREATE TABLE IF NOT EXISTS `biopsy_p0` (
`id` int(11) unsigned NOT NULL,
`biopsy_id` int(11) unsigned NOT NULL,
`introduced` date NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1;
INSERT INTO `biopsy_p0` (`id`, `biopsy_id`, `introduced`) VALUES
(1, 226, '2014-12-31'),
(2, 226, '2014-12-31'),
(3, 226, '2014-12-31'),
(4, 227, '2015-03-14'),
(5, 255, '2015-03-10'),
(6, 255, '2015-03-12');
CREATE TABLE IF NOT EXISTS `biopsy_p1` (
`id` int(11) unsigned NOT NULL,
`biopsy_p0_id` int(11) unsigned NOT NULL,
`introduced` date NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1;
INSERT INTO `biopsy_p1` (`id`, `biopsy_p0_id`, `introduced`) VALUES
(1, 1, '2015-03-18'),
(2, 2, '2015-03-31'),
(3, 1, '2015-03-17'),
(4, 1, '2015-03-18'),
(5, 3, '2015-03-11'),
(6, 2, '2015-03-01');
也许有一个层级关系 table 会更好,它通过 ID 引用另一个 table 存储实际数据...所以我只有两个 table涉及到,如果添加新元素会更灵活...
让我们从声明键开始:
CREATE TABLE IF NOT EXISTS `biopsy` (
`id` int(11) unsigned NOT NULL primary key,
`creation_date` date NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=228 DEFAULT CHARSET=latin1;
INSERT INTO `biopsy` (`id`, `creation_date`) VALUES
(226, '2015-03-08'),
(227, '2015-03-08');
CREATE TABLE IF NOT EXISTS `biopsy_p0` (
`id` int(11) unsigned NOT NULL primary key,
`biopsy_id` int(11) unsigned NOT NULL,
`introduced` date NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1;
alter table biopsy_p0 add constraint fk_biopsy
foreign key (biopsy_id)
references biopsy (id)
on update cascade
on delete cascade;
INSERT INTO `biopsy_p0` (`id`, `biopsy_id`, `introduced`) VALUES
(1, 226, '2014-12-31'),
(2, 226, '2014-12-31'),
(3, 226, '2014-12-31'),
(4, 227, '2015-03-14');
-- violates the f.k. introduced
-- (5, 255, '2015-03-10'),
-- (6, 255, '2015-03-12');
CREATE TABLE IF NOT EXISTS `biopsy_p1` (
`id` int(11) unsigned NOT NULL primary key,
`biopsy_p0_id` int(11) unsigned NOT NULL,
`introduced` date NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1;
alter table biopsy_p1 add constraint fk_biopsy_p0
foreign key (biopsy_p0_id)
references biopsy_p0 (id)
on update cascade
on delete cascade;
INSERT INTO `biopsy_p1` (`id`, `biopsy_p0_id`, `introduced`)
VALUES
(1, 1, '2015-03-18'),
(2, 2, '2015-03-31'),
(3, 1, '2015-03-17'),
(4, 1, '2015-03-18'),
(5, 3, '2015-03-11'),
(6, 2, '2015-03-01');
我建议您按照它们的本来面目来命名,即不要将 id 列命名为 id 并在模型的其他地方更改名称。示例:
CREATE TABLE IF NOT EXISTS biopsy (
biopsy_id int unsigned NOT NULL primary key,
creation_date date NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=228 DEFAULT CHARSET=latin1;
但我会把它放在一边。现在我们知道数据是一致的:
select x.id as biopsy_id, x.creation_date
, y.id as biopsy_p0_id, y.introduced as biopsy_p0_introduction
, z.id as biopsy_p1_id, z.introduced as biopsy_p1_introduction
from biopsy as x
left join biopsy_p0 as y
on y.biopsy_id = x.id
left join biopsy_p1 as z
on z.biopsy_p0_id = y.id
order by x.id, y.id, z.id;
+-----------+---------------+--------------+------------------------+--------------+------------------------+
| biopsy_id | creation_date | biopsy_p0_id | biopsy_p0_introduction | biopsy_p1_id | biopsy_p1_introduction |
+-----------+---------------+--------------+------------------------+--------------+------------------------+
| 226 | 2015-03-08 | 1 | 2014-12-31 | 1 | 2015-03-18 |
| 226 | 2015-03-08 | 1 | 2014-12-31 | 3 | 2015-03-17 |
| 226 | 2015-03-08 | 1 | 2014-12-31 | 4 | 2015-03-18 |
| 226 | 2015-03-08 | 2 | 2014-12-31 | 2 | 2015-03-31 |
| 226 | 2015-03-08 | 2 | 2014-12-31 | 6 | 2015-03-01 |
| 226 | 2015-03-08 | 3 | 2014-12-31 | 5 | 2015-03-11 |
| 227 | 2015-03-08 | 4 | 2015-03-14 | NULL | NULL |
+-----------+---------------+--------------+------------------------+--------------+------------------------+
7 rows in set (0.00 sec)
剩下的只是纯粹的展示,最好在 php 中完成。
对于你的一般性问题,将结构信息保存在一个中是否更好table我会说,如果你有少量固定级别,你的解决方案就可以了。
对于大量级别或如果数量未知,您需要某种递归结构(请注意,您还需要提出此类问题的方法,大多数 DBMS:s 具有 Recursive Common [=现在有 34=] 表达式,但 MySQL 没有。你可以用变量解决某些问题,但很快就会变得混乱)。 Troels Arvin 的链接集合位于:
http://troels.arvin.dk/db/rdbms/links/#hierarchical
您可能会觉得有用。
我有多个 table 都具有相似的基本结构:
biopsy_p0
id | biopsy_id | introduced
biopsy_p1
id | biopsy_p0_id | introduced
biopsy_p2
id | biopsy_p1_id | introduced
我的目标是获得
之间依赖关系的树视图biopsy_p0.id->biopsy_p1.biopsy_p0_id->biopsy_p2.biopsy_p1_id
我试着只用 SQL 来做,但是正如你从我的问题中看到的那样,我在这方面不是很有经验。到目前为止我能找到的所有内容都是关于分层树的参考资料。但是那些总是只使用一个 table 和内部引用。
---更新: 我现在让它与 PHP 一起工作,这确实不是一个好的解决方案,我希望我可以用 SQL 来实现它,这样扩展起来会更好一些:
PHP代码:
$database = DatabaseFactory::getFactory()->getConnection();
// Get all p0 element asociated with the biopsy
$sql = "SELECT *
FROM biopsy_p0
WHERE biopsy_id = :id";
$query = $database->prepare($sql);
$query->execute(array(':id' => $id));
$p0 = $query->fetchAll();
// Get all p1 elements
$sql="SELECT *
FROM biopsy_p0 as p0
RIGHT JOIN biopsy_p1 as p1
ON p0.id=p1.biopsy_p0_id
WHERE biopsy_id = :id;";
$query = $database->prepare($sql);
$query->execute(array(':id' => $id));
$p1 = $query->fetchAll();
for ($i=0; $i < count($p0); $i++)
{
$p1Array = new ArrayObject();
foreach ($p1 as $key => $value)
{
if ($value->biopsy_p0_id == $p0[$i]->id)
{
$p1Array->append($value);
}
$p0[$i]->p1 = $p1Array;
}
unset($p1Array);
}
if ($p0 != NULL){
return $p0;
}
return FALSE;
结果: 这正是我需要的,但是 PHP 很乱,而且它的复杂性随着我想检查的每个子级别而增加。
details: Array
(
[0] => stdClass Object
(
[id] => 1
[biopsy_id] => 226
[introduced] => 2014-12-31
[p1] => ArrayObject Object
(
[storage:ArrayObject:private] => Array
(
[0] => stdClass Object
(
[id] => 1
[biopsy_id] => 226
[introduced] => 2015-03-18
[biopsy_p0_id] => 1
)
[1] => stdClass Object
(
[id] => 3
[biopsy_id] => 226
[introduced] => 2015-03-17
[biopsy_p0_id] => 1
)
[2] => stdClass Object
(
[id] => 4
[biopsy_id] => 226
[introduced] => 2015-03-18
[biopsy_p0_id] => 1
)
)
)
)
[1] => stdClass Object
(
[id] => 2
[biopsy_id] => 226
[introduced] => 2014-12-31
[p1] => ArrayObject Object
(
[storage:ArrayObject:private] => Array
(
[0] => stdClass Object
(
[id] => 2
[biopsy_id] => 226
[introduced] => 2015-03-31
[biopsy_p0_id] => 2
)
[1] => stdClass Object
(
[id] => 6
[biopsy_id] => 226
[introduced] => 2015-03-01
[biopsy_p0_id] => 2
)
)
)
)
[2] => stdClass Object
(
[id] => 3
[biopsy_id] => 226
[introduced] => 2014-12-31
[p1] => ArrayObject Object
(
[storage:ArrayObject:private] => Array
(
[0] => stdClass Object
(
[id] => 5
[biopsy_id] => 226
[introduced] => 2015-03-11
[biopsy_p0_id] => 3
)
)
)
)
)
SQL数据:
CREATE TABLE IF NOT EXISTS `biopsy` (
`id` int(11) unsigned NOT NULL,
`creation_date` date NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=228 DEFAULT CHARSET=latin1;
INSERT INTO `biopsy` (`id`, `creation_date`) VALUES
(226, '2015-03-08'),
(227, '2015-03-08');
CREATE TABLE IF NOT EXISTS `biopsy_p0` (
`id` int(11) unsigned NOT NULL,
`biopsy_id` int(11) unsigned NOT NULL,
`introduced` date NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1;
INSERT INTO `biopsy_p0` (`id`, `biopsy_id`, `introduced`) VALUES
(1, 226, '2014-12-31'),
(2, 226, '2014-12-31'),
(3, 226, '2014-12-31'),
(4, 227, '2015-03-14'),
(5, 255, '2015-03-10'),
(6, 255, '2015-03-12');
CREATE TABLE IF NOT EXISTS `biopsy_p1` (
`id` int(11) unsigned NOT NULL,
`biopsy_p0_id` int(11) unsigned NOT NULL,
`introduced` date NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1;
INSERT INTO `biopsy_p1` (`id`, `biopsy_p0_id`, `introduced`) VALUES
(1, 1, '2015-03-18'),
(2, 2, '2015-03-31'),
(3, 1, '2015-03-17'),
(4, 1, '2015-03-18'),
(5, 3, '2015-03-11'),
(6, 2, '2015-03-01');
也许有一个层级关系 table 会更好,它通过 ID 引用另一个 table 存储实际数据...所以我只有两个 table涉及到,如果添加新元素会更灵活...
让我们从声明键开始:
CREATE TABLE IF NOT EXISTS `biopsy` (
`id` int(11) unsigned NOT NULL primary key,
`creation_date` date NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=228 DEFAULT CHARSET=latin1;
INSERT INTO `biopsy` (`id`, `creation_date`) VALUES
(226, '2015-03-08'),
(227, '2015-03-08');
CREATE TABLE IF NOT EXISTS `biopsy_p0` (
`id` int(11) unsigned NOT NULL primary key,
`biopsy_id` int(11) unsigned NOT NULL,
`introduced` date NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1;
alter table biopsy_p0 add constraint fk_biopsy
foreign key (biopsy_id)
references biopsy (id)
on update cascade
on delete cascade;
INSERT INTO `biopsy_p0` (`id`, `biopsy_id`, `introduced`) VALUES
(1, 226, '2014-12-31'),
(2, 226, '2014-12-31'),
(3, 226, '2014-12-31'),
(4, 227, '2015-03-14');
-- violates the f.k. introduced
-- (5, 255, '2015-03-10'),
-- (6, 255, '2015-03-12');
CREATE TABLE IF NOT EXISTS `biopsy_p1` (
`id` int(11) unsigned NOT NULL primary key,
`biopsy_p0_id` int(11) unsigned NOT NULL,
`introduced` date NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1;
alter table biopsy_p1 add constraint fk_biopsy_p0
foreign key (biopsy_p0_id)
references biopsy_p0 (id)
on update cascade
on delete cascade;
INSERT INTO `biopsy_p1` (`id`, `biopsy_p0_id`, `introduced`)
VALUES
(1, 1, '2015-03-18'),
(2, 2, '2015-03-31'),
(3, 1, '2015-03-17'),
(4, 1, '2015-03-18'),
(5, 3, '2015-03-11'),
(6, 2, '2015-03-01');
我建议您按照它们的本来面目来命名,即不要将 id 列命名为 id 并在模型的其他地方更改名称。示例:
CREATE TABLE IF NOT EXISTS biopsy (
biopsy_id int unsigned NOT NULL primary key,
creation_date date NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=228 DEFAULT CHARSET=latin1;
但我会把它放在一边。现在我们知道数据是一致的:
select x.id as biopsy_id, x.creation_date
, y.id as biopsy_p0_id, y.introduced as biopsy_p0_introduction
, z.id as biopsy_p1_id, z.introduced as biopsy_p1_introduction
from biopsy as x
left join biopsy_p0 as y
on y.biopsy_id = x.id
left join biopsy_p1 as z
on z.biopsy_p0_id = y.id
order by x.id, y.id, z.id;
+-----------+---------------+--------------+------------------------+--------------+------------------------+
| biopsy_id | creation_date | biopsy_p0_id | biopsy_p0_introduction | biopsy_p1_id | biopsy_p1_introduction |
+-----------+---------------+--------------+------------------------+--------------+------------------------+
| 226 | 2015-03-08 | 1 | 2014-12-31 | 1 | 2015-03-18 |
| 226 | 2015-03-08 | 1 | 2014-12-31 | 3 | 2015-03-17 |
| 226 | 2015-03-08 | 1 | 2014-12-31 | 4 | 2015-03-18 |
| 226 | 2015-03-08 | 2 | 2014-12-31 | 2 | 2015-03-31 |
| 226 | 2015-03-08 | 2 | 2014-12-31 | 6 | 2015-03-01 |
| 226 | 2015-03-08 | 3 | 2014-12-31 | 5 | 2015-03-11 |
| 227 | 2015-03-08 | 4 | 2015-03-14 | NULL | NULL |
+-----------+---------------+--------------+------------------------+--------------+------------------------+
7 rows in set (0.00 sec)
剩下的只是纯粹的展示,最好在 php 中完成。
对于你的一般性问题,将结构信息保存在一个中是否更好table我会说,如果你有少量固定级别,你的解决方案就可以了。
对于大量级别或如果数量未知,您需要某种递归结构(请注意,您还需要提出此类问题的方法,大多数 DBMS:s 具有 Recursive Common [=现在有 34=] 表达式,但 MySQL 没有。你可以用变量解决某些问题,但很快就会变得混乱)。 Troels Arvin 的链接集合位于:
http://troels.arvin.dk/db/rdbms/links/#hierarchical
您可能会觉得有用。