2008-09-12 11 views
7

tengo las siguientes tablas. grupos tabla que contiene grupos ordenados jerárquicamente y group_member que almacena a la que los grupos de un usuario pertenece a.SQL Recursion

groups 
--------- 
id 
parent_id 
name 

group_member 
--------- 
id 
group_id 
user_id 

ID PARENT_ID NAME 
--------------------------- 
1 NULL  Cerebra 
2 1   CATS 
3 2   CATS 2.0 
4 1   Cerepedia 
5 4   Cerepedia 2.0 
6 1   CMS 

ID GROUP_ID USER_ID 
--------------------------- 
1 1  3 
2 1  4 
3 1  5 
4 2  7 
5 2  6 
6 4  6 
7 5  12 
8 4  9 
9 1  10 

Deseo recuperar los grupos visibles para un usuario determinado. Eso quiere decir grupos a los que pertenece un usuario e hijos de estos grupos. Por ejemplo, con los datos anteriores:

USER VISIBLE_GROUPS 
9  4, 5 
3  1,2,4,5,6 
12 5 

Estoy obteniendo estos valores usando la recursividad y varias consultas en la base de datos. Pero me gustaría saber si es posible hacerlo con una sola consulta SQL para mejorar el rendimiento de mi aplicación. Estoy usando MySQL.

Respuesta

0

Creo que va a necesitar para esta cursores, this link pueden ayudar a

+0

El enlace codeproject está escrito para SQL Server. Puede que no sea fácil adaptar ese código para mySQL, pero aquí está la referencia mySQL en los cursores: http://dev.mysql.com/doc/refman/5.0/en/cursors.html – Prestaul

0

no creo que esto se puede lograr sin el uso de la recursividad. Puede lograrlo con un solo procedimiento almacenado utilizando mySQL, pero la recursión no está permitida en los procedimientos almacenados de forma predeterminada. This article tiene información sobre cómo habilitar la recursividad. No estoy seguro de cuánto impacto tendrá esto en el rendimiento frente al enfoque de consulta múltiple. mySQL puede hacer algo de optimización de los procedimientos almacenados, pero de otro modo esperaría que el rendimiento fuera similar.

0

no sabía si tenía una tabla de usuarios, por lo que obtener la lista a través de la User_ID de almacenado en la tabla Group_Member ...

SELECT GroupUsers.User_ID, 
     (
     SELECT 
      STUFF((SELECT ',' + 
      Cast(Group_ID As Varchar(10)) 
      FROM Group_Member Member (nolock) 
      WHERE Member.User_ID=GroupUsers.User_ID 
     FOR XML PATH('')),1,1,'') 
     ) As Groups 
FROM (SELECT User_ID FROM Group_Member GROUP BY User_ID) GroupUsers 

que devuelve:

User_ID Groups 
3   1 
4   1 
5   1 
6   2,4 
7   2 
9   4 
10   1 
12   5 

que parece correcto según los datos en su tabla. Pero no coincide con la lista de valores esperados (por ejemplo, el usuario 9 solo está en un grupo en los datos de la tabla pero lo muestra en los resultados como pertenecientes a dos)

EDITAR: Dang. Recién noté que estás usando MySQL. Mi solución fue para SQL Server. Lo siento.

- Kevin Fairchild

-1

No hay manera de hacer esto en el estándar SQL, pero por lo general se pueden encontrar extensiones específicas del proveedor, por ejemplo, CONNECT BY en Oracle.

ACTUALIZACIÓN: Como los comentarios señalan, esto se añadió en SQL 99.

+0

Incorrecto. El estándar ISO SQL ha especificado SQL recursivo desde el estándar SQL: 1999. DB2 y versiones recientes de MSSQL lo implementan. El SQL recursivo del estándar SQL es bastante diferente de CONNECT BY de Oracle, por cierto. –

+0

No me di cuenta de eso. ¿Existe una referencia gratuita para los últimos estándares, ya que ISO aparentemente cree que los desarrolladores deberían pagar por el estándar en sí mismo? –

6

Dos cosas vienen a la mente:

1 - Puede repetidamente de combinación externa de la tabla consigo misma a forma recursiva caminar hasta su árbol, como en:

SELECT * 
FROM 
    MY_GROUPS MG1 
,MY_GROUPS MG2 
,MY_GROUPS MG3 
,MY_GROUPS MG4 
,MY_GROUPS MG5 
,MY_GROUP_MEMBERS MGM 
WHERE MG1.PARENT_ID = MG2.UNIQID (+) 
    AND MG1.UNIQID = MGM.GROUP_ID (+) 
    AND MG2.PARENT_ID = MG3.UNIQID (+) 
    AND MG3.PARENT_ID = MG4.UNIQID (+) 
    AND MG4.PARENT_ID = MG5.UNIQID (+) 
    AND MGM.USER_ID = 9 

Eso te va a dar resultados como esto:

UNIQID PARENT_ID NAME  UNIQID_1 PARENT_ID_1 NAME_1 UNIQID_2 PARENT_ID_2 NAME_2 UNIQID_3 PARENT_ID_3 NAME_3 UNIQID_4 PARENT_ID_4 NAME_4 UNIQID_5 GROUP_ID USER_ID 
4  2   Cerepedia 2  1   CATS 1  null  Cerebra null  null  null null  null  null 8  4  9 

El límite aquí es que usted debe agregar una nueva combinación para cada "nivel" que desea subir el árbol. Si su árbol tiene menos de, digamos, 20 niveles, entonces probablemente pueda salirse con la suya creando una vista que muestre 20 niveles de cada usuario.

2 - El único otro enfoque que conozco es crear una función de base de datos recursiva y llamarla desde el código. De todos modos, tendrá cierta sobrecarga de búsqueda (es decir, su número de consultas seguirá siendo igual al número de niveles que está caminando en el árbol), pero en general debería ser más rápido, ya que todo se lleva a cabo dentro de la base de datos.

No estoy seguro acerca de MySql, pero en Oracle, tal función sería similar a esta (tendrá que cambiar los nombres de tabla y campo; estoy copiando algo que hice en el pasado) :

CREATE OR REPLACE FUNCTION GoUpLevel(WO_ID INTEGER, UPLEVEL INTEGER) RETURN INTEGER 
IS 
BEGIN 
    DECLARE 
    iResult INTEGER; 
    iParent INTEGER; 
BEGIN 
    IF UPLEVEL <= 0 THEN 
    iResult := WO_ID; 
    ELSE 
    SELECT PARENT_ID 
    INTO iParent 
    FROM WOTREE 
    WHERE ID = WO_ID;  
    iResult := GoUpLevel(iParent,UPLEVEL-1); --recursive 
    END; 
    RETURN iResult; 
    EXCEPTION WHEN NO_DATA_FOUND THEN 
    RETURN NULL; 
    END; 
END GoUpLevel; 
/
3

libros de Joe Cleko "SQL para Smarties" y "árboles y jerarquías en SQL para Smarties" describen métodos que eviten la repetición del todo, mediante el uso de conjuntos anidados. Eso complica la actualización, pero hace que otras consultas (que normalmente necesitarían recursión) sean comparativamente sencillas. Hay some examples in this article escrito por Joe en 1996.

0

Ya hubo question similar ya planteado.

Aquí es mi respuesta (un poco editado):

No estoy seguro de entender correctamente su pregunta, pero esto podría funcionar My take on trees in SQL.

Publicación vinculada describe el método de almacenamiento de árbol en la base de datos - PostgreSQL en ese caso - pero el método es lo suficientemente claro, por lo que se puede adoptar fácilmente para cualquier base de datos.

Con este método puede actualizar fácilmente todos los nodos que dependen del nodo modificado K con aproximadamente N consultas SELECT simples donde N es la distancia de K desde el nodo raíz.

¡Buena suerte!

0

No recuerdo en qué pregunta SO encontré el enlace, pero this article on sitepoint.com (segunda página) muestra otra forma de almacenar árboles jerárquicos en una tabla que facilita la búsqueda de todos los nodos secundarios, o la ruta hacia la parte superior , ese tipo de cosas. Buena explicación con código de ejemplo.


PS. Newish to StackOverflow, ¿está bien la respuesta anterior, o debería haber sido realmente un comentario sobre la pregunta, ya que es solo un indicador de una solución diferente (que no responde exactamente la pregunta en sí)?