2011-05-28 17 views
5

Tengo una representación en árbol en la tabla mysql basada en id, depth, parent_id y path. Cada registro raíz dentro de esta tabla tiene una profundidad de 0, parent_id != null y path representación basada en el valor hexadecimal de la ID rellenada a la izquierda con 0.cadena más corta dentro de la misma ruta (rama)

Cada elemento del árbol se construye mediante la especificación de depth = parent.depth + 1, path = parent.path + hex(id), parent_id = parent.id (pseudo código), por ejemplo:

id path   depth parent_id assigned_user_id 
------------------------------------------------------------ 
1  001    0  NULL   NULL 
2  002    0  NULL   1 
3  001003   1  1   2 
4  002004   1  2   1 
5  001003005  2  3   2 
6  001003005006 3  5   2 
7  002004007  2  4   1 
8  002004008  2  4   2 
9  002004009  2  4   2 
10 00200400800A 3  8   2 

y así sucesivamente ... El problema es cómo conseguir los registros de usuario específico Identificación limitada a la ruta más corta en la misma rama. Por ejemplo, para assigned_user_id = 2 retrive:

id path   depth parent_id assigned_user_id 
------------------------------------------------------------ 
3  001003   1  1   2 
8  002004008  2  4   2 
9  002004009  2  4   2 

En lugar de:

id path   depth parent_id assigned_user_id 
------------------------------------------------------------ 
3  001003   1  1   2 
5  001003005  2  3   2 
6  001003005006 3  5   2 
8  002004008  2  4   2 
9  002004009  2  4   2 
10 00200400800A 3  8   2 
+0

No entiendo. Usted escribe que necesita la ruta más corta, pero en su salida deseada, hay múltiples registros con diferentes longitudes de ruta. Sea más claro sobre cuáles son sus criterios para seleccionar los registros. – Krab

+0

@Krab El inicio de las rutas debe ser igual. Gracias por el comentario, actualizaré mi pregunta. – veritas

+1

Básicamente, creo que entiendo la pregunta. Me gustaría que elabores sobre esta situación. Supongamos que los elementos definidos por las rutas '001003' y' 001003005006' se asignan al usuario '2', y' 001003005' se asigna al usuario '1'. ¿Sería correcto descartar '001003005006' simplemente con el argumento de que comienza con' 001003'? En otras palabras, ¿se debe tener en cuenta la adyacencia de los niveles (profundidades)? –

Respuesta

2
SELECT t1.* 
FROM atable t1 
    LEFT JOIN atable t2 
    ON t2.assigned_user_id = t1.assigned_user_id AND 
     t2.path = LEFT(t1.path, CHAR_LENGTH(t2.path)) AND 
     t2.id <> t1.id 
WHERE t1.assigned_user_id = 2 
    AND t2.id IS NULL 
+0

Me parece que es la solución más óptima que http://stackoverflow.com/questions/6162527/shortest-string-within-same-path-branch/6162874#6162874 (que es también válido) – veritas

0

¿Has probado algo como esto?

select child.assigned_user_id, child.id 
from node as child 
left join node as parent 
on child.path like CONCAT(parent.path, '%') 
and child.assigned_user_id = parent.assigned_user_id 
and child.id <> parent.id 
group by child.assigned_user_id, child.id 
having max(parent.id is null) = true 

(No estoy seguro de que va a funcionar exactamente como el anterior, pero básicamente: a la izquierda se unen en el camino con el fin de extraer la lista completa de los padres y, a continuación, agregar de tal manera que sólo se mantiene el . nodos sin ningún tipo de los padres cuando se agrupan por assigned_user_id)

1

me gustaría probar algo como esto:

SELECT * FROM PATHS WHERE ASSIGNED_USER_ID = 2 
AND NOT PARENT_ID IN (SELECT ID FROM PATHS WHERE ASSIGNED_USER_ID = 2) 

Básicamente la idea es seleccionar los mejores nodos padre para el usuario dado.

2

Si entiendo bien, puede ser suficiente excluir filas cuyo parent_id se encuentra entre los identificadores seleccionados. Esto se debe a que si se seleccionan el padre y el hijo, deben estar en la misma rama. La ruta del padre será más corta, por lo tanto, está bien excluir al niño.

Algo así como:

SELECT * 
    FROM x 
    WHERE assigned_user_id = 2 
     AND parent_id NOT IN (SELECT id FROM x WHERE assigned_user_id = 2) 

Si desea tener un árbol como este (números son su id de usuario asignados):

A1     G2 
/\     /\ 
B2 C2    H2 I2 
    | \    | | \ 
    D2 E2   L1 J2 K2 
         | 
         M2 

B2, C2, G2 y M2 serían seleccionados. Sin embargo, todavía no estoy seguro de si esta era tu intención.

+0

Tenía esta idea, también, pero no funciona. Ejemplo: Imagine que tiene un nodo N2 como hijo de M2. – Wolfgang

+0

Agradable, +1. Estaba pensando en procesar la cadena de ruta y tener en cuenta el nivel de profundidad, pero su enfoque parece mucho más simple. Me gusta más. –

+0

@ Wolfgang: Por qué, no creo que aparezca 'N2' con este script. –

1

Idea detrás de esto: B es más corto que A si A comienza con B. Quizás haya algo mejor que LIKE para hacer esto "comienza con".

SELECT a.* FROM node AS a 
WHERE a.assigned_user_id = ? 
AND NOT EXIST 
(SELECT * FROM node AS b 
    WHERE b.assigned_user_id = ? 
    AND LENGTH(a.path) > LENGTH(b.path) 
    AND a.path LIKE CONCAT(b.path, '%')) 

Ambos? se asignan a la identificación de usuario deseada.

EDITAR

olvidó incluir la assigned_user_id. Cambió el código.

segundo EDITAR

código modificado para evitar el caso de b = a.

+0

Resultado vacío :( – veritas

+0

Ouch, tienes razón. La selección interna también encontrará b = a. Intentaré solucionarlo. – Wolfgang

+0

Trabajo 100% :) Pero, estoy analizando cada solución – veritas

Cuestiones relacionadas