Отображение всех детей каждого родителя
У меня есть таблица People
. Я хочу отобразить таблицу HTML, состоящую из каждого родителя, со всеми их дочерними элементами непосредственно под ними.
_________
|People |_____________________________________________
|-------------------------------------------------------|
| id | parent | firstname | lastname |
|-------------------------------------------------------|
| 1 0 James Donovan |
| 2 0 Jeffrey Williams |
| 3 0 Emmit Herring |
| 4 2 Carol Williams |
| 5 2 Sarah Williams |
| 6 1 Nikolai Donovan |
|_______________________________________________________|
Ожидаемый результат:
________________________________________________
|Jeffrey Williams |
|------------------------------------------------|
| - Carol Williams |
| - Sarah Williams |
|________________________________________________|
|James Donovan |
|------------------------------------------------|
| - Nikolai Donovan |
|________________________________________________|
|Emmit Herring |
|------------------------------------------------|
|________________________________________________|
Как мне создать ассоциативный массив, содержащий правильный набор результатов, для повторения? Я запутался в правильном SQL и правильном PHP для создания финального массива.
В частности, я не уверен, как отображать иерархическую взаимосвязь между двумя таблицами MySQL. Насколько я знаю, наборы результатов SQL не являются многомерными. Включение SQL-запроса в цикл for
ужасно для производительности. Итак, что вы делаете?
Я предполагаю, что я ищу реализацию списка смежности в MySQL.
Этот вопрос должен быть легким, если бы я мог разделить все на две таблицы, но, к сожалению, я должен придерживаться этой ненормальной структуры таблиц.
Ответы
Ответ 1
Есть несколько способов сделать это:
1. Очевидным является получение списка всех родителей, а затем запуск отдельного запроса для дочерних элементов каждого родителя в цикле. Вы говорите, что это "ужасно для производительности", но этого действительно не должно быть, предполагая, что у вас есть индекс в столбце parent
и что ваш сервер MySQL не расположен на другой стороне планеты.
2. Если вы действительно хотите это сделать в одном запросе, вы можете использовать LEFT JOIN
в таблице против себя:
SELECT
p.id AS parent_id,
p.firstname AS parent_firstname,
p.lastname AS parent_lastname,
c.id AS child_id,
c.firstname AS child_firstname,
c.lastname AS child_lastname
FROM
People AS p
LEFT JOIN People AS c ON c.parent = p.id
WHERE p.parent = 0
ORDER BY p.id
Опять же, вам действительно нужен индекс в столбце parent
. Предложение ORDER BY
должно гарантировать, что дети каждого родителя будут отсортированы вместе; вы можете изменить его, например. на что-то вроде p.lastname, p.firstname, p.id, c.lastname, c.firstname, c.id
, если вы хотите, чтобы имена отсортировались в алфавитном порядке. В PHP вам необходимо выполнить цикл над результатами и напечатать новый заголовок всякий раз, когда изменяется родительский идентификатор (и не забудьте обработать случай, когда столбцы child_*
NULL), что-то вроде:
$res = mysql_query( $sql );
$last_parent_id = 0;
while ( $row = mysql_fetch_object( $res ) ) {
if ( $row->parent_id != $last_parent_id ) {
// print parent header
$last_parent_id = $row->parent_id;
}
if ( $row->child_id ) {
// print child row
}
}
3. Третий вариант - просто получить все строки с простым запросом SELECT * FROM People
и построить дерево в PHP:
$res = mysql_query( "SELECT * FROM People" ); // add WHERE clauses if needed
$names = array();
$parents = array();
$children = array();
while ( $row = mysql_fetch_object( $res ) ) {
$names[ $row->id ] = array( $row->firstname, $row->lastname );
if ( $row->parent == 0 ) {
$parents[] = $row->id;
} else {
if ( !array_key_exists( $row->parent, $children ) )
$children[ $row->parent ] = array();
$children[ $row->parent ][] = $row->id;
}
}
foreach ( $parents as $parent_id ) {
// print parent header
if ( array_key_exists( $parent_id, $children ) ) {
foreach ( $children[ $parent_id ] as $child_id ) {
// print child row
}
}
}
Ps. Если вы действительно не хотите показывать всех родителей и детей в таблице, но, скажем, те, которые принадлежат к одному семейству, тогда вы все равно должны попытаться выполнить фильтрацию в SQL, чтобы избежать слишком большого количества записей.
Ответ 2
Согласно традиционному подходу, я думаю, что, начиная с SQL, объединение таблиц (даже если левая таблица и правая таблица в этом случае будут одинаковыми), может быть хорошей отправной точкой.
Это в основном потому, что с помощью СУБД вы всегда должны иметь дело с табличными структурами и соединяться с таблицами так, как вам гарантируется согласованность данных.
Итак, начните с чего-то вроде:
SELECT
a.id parent_id, a.firstname parent_name, a.lastname parent_lastname,
b.id child_id, b.firstname child_firstname, b.lastname child_lastname
FROM
People a LEFT OUTER JOIN People b ON a.id = b.parent
WHERE
a.parent = 0;
Во-вторых, вам следует использовать стратегию "fetch_all" (например, с расширением mysqli
php, но она также доступна с помощью PDO
), что даст вам возможность с одной единственной операцией иметь целое набор результатов, полученный в двумерном ассоциативном массиве.
На этом этапе вы можете выбрать свой путь.
All-PHP: вы можете пройти массив с помощью PHP и напрямую создать разметку представления, чтобы отобразить данные, организованные по мере необходимости, echo
в строку html в сторону браузера.
AJAX: если - например, ваш PHP script был скорее вызван через AJAX, вы могли бы также пройти массив результатов запроса, но на этот раз интерпретируя его для построения структуры JSON, с которой вы ответили бы на вызов, просто так:
{
"1": {
"id": 1,
"firstname": "James",
"lastname": "Donovan",
"children": {
"6": {
"id": 6,
"firstname": "Nikolai",
"lastname": "Donovan"
}
}
},
"2": {
"id": 2,
"firstname": "Jeffrey",
"lastname": "Williams",
"children": {
"4": {
"id": 4,
"firstname": "Carol",
"lastname": "Williams"
},
"5": {
"id": 5,
"firstname": "Sarah",
"lastname": "Williams"
}
}
},
"3": {
"id": 3,
"firstname": "Emmit",
"lastname": "Herring",
"children": { }
}
}
Такое представление было бы лучше для обмена данными, потому что ваш javascript на стороне клиента мог бы легко распознать его и пройти его, чтобы заполнить ранее существовавший пустой скелет таблицы. Уверен, что вы могли бы напрямую использовать PHP для json_encode()
массива результатов, а не перестраивать его на что-то еще подобное, но вы оказались бы с чем-то, что не продвигалось бы гораздо дальше, чем у массивного представления массива, подобного массиву записей.
Наконец, решение all-mysql должно было бы подготовить хранимую процедуру, которая целенаправленно создает структуру данных, которую вы ищете, например. 1 строка на семью, с полным именем родителя в качестве первого столбца и полными именами детей в качестве последующих столбцов (пустые поля, если у человека нет дочерних элементов, например Emmit Herring).
Вы можете снова "fetch_all" набор результатов с PHP, пройти массив, и все будет готово.
Поэтому, если производительность является проблемой, этот последний подход должен гарантировать вам наилучшие результаты, даже если нужно сказать, что цена оплачивается сервером с точки зрения вычислительной нагрузки и использования памяти, если вы собираетесь заниматься с огромным количеством данных.
Ответ 3
Вы можете использовать цикл в цикле:
$res = mysql_query("SELECT PARENT");
while( $row = mysql_fetch_assoc($res) )
{
// echo parent
$res2 = mysql_query("SELECT CHILD WHERE PARENT SOMETHING");
while( $row2 = mysql_fetch_assoc($res2) )
{
// echo child
}
}
Или сохраните его позже и сохраните флаг.
$people = array();
$res = mysql_query("SELECT PARENT");
while( $row = mysql_fetch_assoc($res) )
{
$people[] = array('is_parent' => true,
'info' => $row);
$res2 = mysql_query("SELECT CHILD WHERE PARENT SOMETHING");
while( $row2 = mysql_fetch_assoc($res2) )
{
$people[] = array('is_parent' => false,
'info' => $row2);
}
}
// later
foreach( $people as $person )
{
if( $person['is_parent'] )
{
// echo parent
}
else
{
// echo child
}
}
Ответ 4
Почему бы не создать многолинейный массив в JavaScript? После этого просто пропустите массив, чтобы получить результаты в DOM.