Получить иерархическое дерево из разных ссылочных таблиц с помощью SQL и переполнения стека

У меня есть несколько таблиц с похожей базовой структурой:

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, но, как вы можете видеть из моего вопроса, я не очень опытен в этом. Все, что я смог найти, это ссылки об иерархических деревьях. Но те используют только одну таблицу с внутренними ссылками.

— Обновить:
Теперь я начал работать с 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');

Возможно, было бы лучше иметь одну таблицу иерархических отношений, которая посредством идентификатора ссылается на другую таблицу, в которой хранятся фактические данные … так что у меня есть только две таблицы, и будет более гибким, если будут добавлены новые элементы …

0

Решение

Давайте начнем с объявления ключей:

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.

На ваш общий вопрос, лучше ли хранить структурную информацию в одной таблице, я бы сказал, что если у вас есть небольшое количество фиксированных уровней, ваше решение подойдет.

Для большого количества уровней или, если число неизвестно, вам нужна какая-то рекурсивная структура (обратите внимание, что вам также понадобятся средства для того, чтобы задавать подобные вопросы, большинство СУБД в наши дни имеют выражения Recursive Common Table, а MySQL нет. Вы можете решить некоторые вещи с помощью переменных, но это довольно быстро запутывается). Троэльс Арвин имеет коллекцию ссылок на:

http://troels.arvin.dk/db/rdbms/links/#hierarchical

что вы можете найти полезным.

0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]