2011-11-01 16 views
12

Tengo una tabla People. Quiero mostrar una tabla HTML que consta de todos los padres, con todos sus hijos directamente debajo de ellos.Mostrando todos los hijos de cada padre

_________ 
|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    | 
|_______________________________________________________| 

Resultados previstos:

________________________________________________ 
|Jeffrey Williams        | 
|------------------------------------------------| 
| - Carol Williams        | 
| - Sarah Williams        | 
|________________________________________________| 
|James Donovan         | 
|------------------------------------------------| 
| - Nikolai Donovan        | 
|________________________________________________| 
|Emmit Herring         | 
|------------------------------------------------| 
|________________________________________________| 

¿Cómo construir una matriz asociativa que contiene el resultado conjunto adecuado para repetir? Estoy confundido acerca del SQL correcto y el PHP correcto para construir el conjunto final.

Específicamente, no estoy seguro de cómo mostrar una relación jerárquica entre dos tablas MySQL. Los conjuntos de resultados SQL no son multidimensionales por lo que yo sé. Poner una consulta SQL en un bucle for es terrible para el rendimiento. Entonces, ¿Qué haces?

Supongo que estoy buscando una implementación de lista de adyacencia en MySQL.

Esta pregunta debería ser fácil si pudiera dividir todo en dos tablas, pero desafortunadamente tengo que seguir con esta estructura de tabla anormal.

+0

Puede usar jquery ¿verdad? – defau1t

+0

¿Por qué debería tener que usar jQuery para mostrar algo tan simple como los elementos secundarios de cada elemento primario? ¿No hay una solución PHP aquí? –

+0

@Mark Supongo que mi pregunta no era lo suficientemente clara. No estoy aquí para ayudar con la tarea. Actualicé la pregunta. –

Respuesta

13

Hay varias maneras de hacerlo:

1. La más obvia es a buscar primero una lista de todos los padres, y luego ejecutar una consulta separada para los hijos de cada padre en una lazo. Usted dice que esto es "terrible para el rendimiento", pero en realidad no debería ser, suponiendo que tiene un índice en la columna parent y que su servidor MySQL no se encuentra en el otro lado del planeta.


2. Si realmente quiere hacer esto es en una sola consulta, se puede utilizar un LEFT JOIN sobre la mesa contra sí misma:

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 

Una vez más, realmente, realmente necesita un índice en la columna parent. La cláusula ORDER BY está ahí para garantizar que los hijos de cada padre se clasifiquen juntos; puedes cambiarlo, p. a algo como p.lastname, p.firstname, p.id, c.lastname, c.firstname, c.id si desea ordenar los nombres por orden alfabético. En PHP, este caso es necesario un bucle sobre los resultados e imprimir una nueva cabecera cada vez que cambia el ID de los padres (y recordar para manejar el caso en que los child_* columnas son NULL), algo así como:

$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. La tercera opción es simplemente captar todas las filas con una simple consulta SELECT * FROM People y construir el árbol en 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 
     } 
    } 
} 

Sal. Si no desea mostrar todos los padres e hijos en la tabla, sino solo los que pertenecen a una sola familia, debe intentar hacer el filtrado en SQL para evitar obtener demasiados registros.

0

¿Por qué no crear una matriz multilineal en JavaScript? Después de eso, simplemente recorre el conjunto para obtener los resultados en el DOM.

+1

Debido a que los usuarios del sitio web no deberían tener la obligación de tener Javascript habilitado. ¿Por qué no puedes mostrar una lista de relaciones entre padres e hijos sin recurrir a manipular el DOM con Javascript? Estoy usando PHP y MySQL. –

1

Se puede usar un bucle en un bucle:

$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 
    } 
} 

O, guardarlo para más tarde, y almacenar una bandera.

$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 
    } 
} 
+0

Claro que podría, pero como mencioné en mi pregunta, hacerlo de esa manera lleva a problemas de rendimiento inaceptables a medida que su mesa crece. –

2

Según un enfoque tradicional, creo que comenzar con SQL, unir tablas (incluso si la tabla izquierda y la tabla derecha en este caso serían las mismas), podría ser un buen punto de partida.

Esto se debe principalmente a que al usar un RDBMS siempre debe tratar con estructuras tabulares, y unir las tablas de esa manera se garantiza la coherencia de los datos.

lo tanto, empezar con algo como:

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; 

En segundo lugar, se debe preferir el uso de una estrategia de "fetch_all" (con mysqli extensión php, por ejemplo, pero está disponible con PDO también), lo que dará tiene la capacidad, con una sola operación, de obtener todo el conjunto de resultados en una matriz asociativa bidimensional.

En este momento puede elegir su ruta.

All-PHP: puede recorrer la matriz con PHP y construir directamente el marcado de la presentación para mostrar los datos organizados como necesite, echo ing la cadena html hacia el navegador.

AJAX: si, por ejemplo, su script PHP se ha llamado a través de AJAX, también podría recorrer el conjunto de resultados de la consulta, pero esta vez interpretándolo para crear una estructura JSON con la que respondería a la llamada, solo así:

{ 
    "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": { } 
    } 
} 

Tal representación sería mejor para el intercambio de datos, debido a que su JavaScript en el cliente podía reconocer sin problemas y caminar a poblar un esqueleto mesa vacía preexistente. Claro que podrías tener PHP en json_encode() en la matriz de resultados directamente en lugar de reestructurarlo en otra cosa como esta, pero te encontrarás con algo que no iría mucho más lejos que la representación de matriz sólida similar a un conjunto de registros que ya tienes.

Por último, la solución all-mysql sería preparar un procedimiento almacenado que construya deliberadamente la estructura de datos que está buscando, p. 1 fila por familia, con el nombre completo del padre como primera columna y los nombres completos de los hijos como columnas posteriores (campos vacíos si la persona no tiene hijos, como Emmit Herring).

Podrías volver a "buscar" todo el conjunto de resultados con PHP, recorrer la matriz y listo.

Por lo tanto, si el rendimiento es una preocupación, este último enfoque debería garantizarle los mejores resultados, incluso si debe decirse que el servidor paga un precio en términos de carga computacional y ocupación de la memoria, si va a lidiar con enormes cantidades de datos.

Cuestiones relacionadas