Подтвердить что ты не робот

Отображение всех детей каждого родителя

У меня есть таблица 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.

Этот вопрос должен быть легким, если бы я мог разделить все на две таблицы, но, к сожалению, я должен придерживаться этой ненормальной структуры таблиц.

4b9b3361

Ответ 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.