2011-06-09 13 views
7

He decidido seguir http://www.artfulsoftware.com/mysqlbook/sampler/mysqled1ch20.html¿Tratar con conjuntos anidados en mysql?

Así que ahora estoy buscando ayuda con el código.

estoy usando sus datos para mis pruebas, Así, visualicé el árbol de ser de esta manera:

array('value' => 'Richard Shakespeare', 
     array('value' => 'Henry', 
      array('value' => 'Joan'), 
      array('value' => 'Margaret'), 
      array('value' => 'William', 
       array('value' => 'Susana', 
        array('value' => 'Elizabeth Hall', 
         array('value' => 'John Bernard'))), 
       array('value' => 'Hamnet'), 
       array('value' => 'Judith', 
        array('value' => 'Shakespeare Quiney'), 
        array('value' => 'Richard Quiney'), 
        array('value' => 'Thomas Quiney'))), 
      array('value' => 'Gilbert'), 
      array('value' => 'Joan', 
       array('value' => 'William Hart'), 
       array('value' => 'Mary Hart'), 
       array('value' => 'Thomas Hart'), 
       array('value' => 'Micheal Hart')), 
      array('value' => 'Anne'), 
      array('value' => 'Richard'), 
      array('value' => 'Edmond')), 
     array('value' => 'John')); 

Así que si queremos insertar esto en la base de datos que queremos terminar con

Array 
(
    [0] => Array 
     (
      [value] => Richard Shakespeare 
      [left] => 1 
      [right] => 46 
     ) 

    [1] => Array 
     (
      [value] => Henry 
      [left] => 2 
      [right] => 43 
     ) 

    [2] => Array 
     (
      [value] => Joan 
      [left] => 3 
      [right] => 4 
     ) 

    [3] => Array 
     (
      [value] => Margaret 
      [left] => 5 
      [right] => 6 
     ) 

    [4] => Array 
     (
      [value] => William 
      [left] => 7 
      [right] => 24 
     ) 

    [5] => Array 
     (
      [value] => Susana 
      [left] => 8 
      [right] => 13 
     ) 

    [6] => Array 
     (
      [value] => Elizabeth Hall 
      [left] => 9 
      [right] => 12 
     ) 

    [7] => Array 
     (
      [value] => John Bernard 
      [left] => 10 
      [right] => 11 
     ) 

    [8] => Array 
     (
      [value] => Hamnet 
      [left] => 14 
      [right] => 15 
     ) 

    [9] => Array 
     (
      [value] => Judith 
      [left] => 16 
      [right] => 23 
     ) 

    [10] => Array 
     (
      [value] => Shakespeare Quiney 
      [left] => 17 
      [right] => 18 
     ) 

    [11] => Array 
     (
      [value] => Richard Quiney 
      [left] => 19 
      [right] => 20 
     ) 

    [12] => Array 
     (
      [value] => Thomas Quiney 
      [left] => 21 
      [right] => 22 
     ) 

    [13] => Array 
     (
      [value] => Gilbert 
      [left] => 25 
      [right] => 26 
     ) 

    [14] => Array 
     (
      [value] => Joan 
      [left] => 27 
      [right] => 36 
     ) 

    [15] => Array 
     (
      [value] => William Hart 
      [left] => 28 
      [right] => 29 
     ) 

    [16] => Array 
     (
      [value] => Mary Hart 
      [left] => 30 
      [right] => 31 
     ) 

    [17] => Array 
     (
      [value] => Thomas Hart 
      [left] => 32 
      [right] => 33 
     ) 

    [18] => Array 
     (
      [value] => Micheal Hart 
      [left] => 34 
      [right] => 35 
     ) 

    [19] => Array 
     (
      [value] => Anne 
      [left] => 37 
      [right] => 38 
     ) 

    [20] => Array 
     (
      [value] => Richard 
      [left] => 39 
      [right] => 40 
     ) 

    [21] => Array 
     (
      [value] => Edmond 
      [left] => 41 
      [right] => 42 
     ) 

    [22] => Array 
     (
      [value] => John 
      [left] => 44 
      [right] => 45 
     ) 

) 

Así que el problema viene a la mente de, ¿cuál es la mejor manera de hacerlo?

Mi solución fue:

$container = array(); 

function children($item){ 
    $children = 0; 
    foreach($item as $node) 
    if(is_array($node)) 
     $children += children($node)+1; 
    return $children; 
} 

function calculate($item, &$container, $data = array(0,0)){ 
    //althought this one is actually of no use, it could be useful as it contains a count 
    $data[0]++; //$left 

    $right = ($data[0]+(children($item)*2))+1; 

    //store the values in the passed container 
    $container[] = array(
    'value' => $item['value'], 
    'left' => $data[0], 
    'right' => $right, 
); 

    //continue looping 
    $level = $data[1]++; 
    foreach($item as &$node) 
    if(is_array($node)) 
     $data = calculate($node, $container, $data); 

    $data[1] = $level; 
    $data[0]++; 
    return $data; 
} 

calculate($tree, $container); 

¿Qué tan eficiente es que no sé.

Pero ahora a las consultas.

Para seleccionar todos los descendientes de un nodo podemos utilizar

SELECT child.value AS 'Descendants of William', COUNT(*) AS `Level` 
FROM tester AS parent 
JOIN tester AS child ON child.`left` BETWEEN parent.`left` AND parent.`right` 
WHERE parent.`left` > 7 AND parent.`right` < 24 
GROUP BY child.value ORDER BY `level`; 

Para seleccionar todos los descendientes de un nodo, a una profundidad específica podemos utilizar
Tenga en cuenta que estamos seleccionando descendientes de William a una profundidad de 2
Williams dejó: 7, Williams derecha: 24, Niveles: 2

SELECT child.value AS 'Descendants of William', COUNT(*) AS `Level` 
FROM tester AS parent 
JOIN tester AS child ON child.`left` BETWEEN parent.`left` AND parent.`right` 
WHERE parent.`left` > 7 AND parent.`right` < 24 
GROUP BY child.value HAVING `level` <= 2 ORDER BY `level`; 

Así que eso es bastante fácil.

Pero ahora quiero saber algunas cosas,
Nótese que en la base de datos actual, así como a la izquierda/derecha todas las filas tienen un identificador único, y una columna de "padre" que contiene su inviteers identificación, o null si no invitó

  • Digamos que quiero insertar David como un hijo de Judith, ¿Cómo lo hago?
  • Digamos que quiero obtener Mary Hart's padre, y el padre de los padres (array('Henery', 'Joan', 'Mary Hart')), ¿cómo puedo hacer eso?
  • Digamos que quiero eliminar William Hart de Joan ¿Cómo hago eso?
+0

¿Qué has probado? Odio preguntar, pero los conjuntos anidados son complejos, y tu pregunta parece que estás haciendo un crowdsourcing de tu trabajo. Además, ni siquiera has comenzado a desgranar la dificultad del problema. Tales como: "si quiero invertir David y Judith, de manera que Judith es ahora toma el lugar de David como hijo y viceversa, ¿cómo puedo hacer que [sin volver a indexar el árbol dos veces]?" –

Respuesta

1

Para actualizar/eliminar necesitarás aumentar/disminuir left/right valores de todos los elementos de la bifurcación.
Ejemplos de consultas que puedes encontrar here.

¿Qué tan eficiente es que no sé.

Conjuntos anidados funciona MUY despacio con árboles grandes en update/insert/delete. Y muy rápido para seleccionar.
a fin de utilizar este modelo sólo con datos estáticos, que son almacenadas sin cambios la mayoría de las veces, y este árbol no contendrá miles de nodos (o cualquier actualización tendrá minutos para completar). La ruta materializada funciona mucho más rápido.

+0

Por lo tanto, si estoy trabajando con una tabla de usuario, por lo tanto, los datos se insertan a menudo, habrá cientos de miles de filas con el tiempo, pero no se actualizará/borrado a menudo, pero será leído en casi cada carga de la página (la estructura del árbol es una parte central de nuestro sitio) ¿debo ir con conjuntos anidados? – Hailwood

+0

@OZ, de hecho, no sólo los elementos de la rama, pero todos los elementos con un left_id> current_element.right_id. – malko

+0

@OZ_: Te invité porque esto no es cierto para MySQL. Tal vez es cierto para PostGreSql y también Celco está por aquí. – Bytemain

0

Todas sus preguntas se pueden resolver muy fácilmente si usa un DFS para cada relación y luego usa su función calculate() nuevamente si lo quiere más detallado.

+0

Disculpe, ¿qué es un DFS? – Hailwood

+0

Hailwood: Profundidad de búsqueda. – Bytemain

1
  • para llegar a los padres de un nodo necesita nodos con left_id < child.left_id y right_id> child.right_id, si sólo desea que el antepasado directo de elegir el conjunto anterior con la más alta left_id.

  • para eliminar un nodo de quitarlo y luego más abajo dos veces todos los identificadores de izquierda/derecha que son mayores de borrado elemento de identificación correcta. if (leftId> deleted.leftId) leftId- = 2 igual para rightId

  • para insertar un nodo, haga espacio para agregar + 2 a todos los nodos con leftId> parent.rightId luego parent.rightId + = 2 luego inserte nodo con leftId = parent.rightId-2 y rightId = parent.rightId-1

Cuestiones relacionadas