Vinzenz Mai: Suche Hilfe bei MySQL-Abfrage - gelöst?

Beitrag lesen

Hallo Werner,

Gegeben sind mir die Gruppen-Ids der Untergruppen von den Gruppen, die Zugriff auf das Modul haben.
Habe ich noch etwas übersehen?

Deine vorgestellte Lösung konnte ich wegen fehlender Parameterwerte nicht nachvollziehen. Im weiteren zeige ich meinen Lösungsvorschlag, der auf meinem Verständnis Deiner Beschreibung basiert.

Ich gehe im folgenden davon aus, dass es genau einen Parameter gibt: die userId des Benutzers, für den wir die Liste der Benutzer sehen wollen, die er administrieren darf (diese ist leer, wenn der Benutzer nicht in einer Gruppe ist, die Zugriff auf die Benutzerverwaltung haben).

a) Liste der echten Untergruppen der Gruppen, in denen sich der Benutzer befindet:

-- Echte Untergruppen haben einen lKey, der größer ist als der lKey
-- der Elterngruppe und kleiner ust als der rKey.
-- Dass Gruppen mehrfach aufgeführt werden, soll uns nicht stören.
-- Erst die Benutzer wollen wir nur noch einfach sehen

SELECT  
    child.groupId  
FROM  
    groups node  
INNER JOIN  
    groups child  
ON  
    child.lKey > node.lKey AND child.lKey < node.rKey  
WHERE  
    -- wir benötigen die Untergruppen aller Gruppen,  
    -- in denen sich der Benutzer befindet  
    node.groupId IN (  
        SELECT  
            groupId  
        FROM  
            group_user  
        WHERE  
            userId = ?  
    )  

Liefert für den userId-Wert 1 (Uwe) als Ergebnis

groupId
-------
    2
    3
    4
    5

/*
    Komplizierter ist das Ausschlussverfahren:
    Hier dürfen nicht einfach alle Vorfahren aller Gruppen
    ausgeschlossen werden, in denen sich der Benutzer befindet,
    eingeschlossen der Gruppe selbst - sondern nur die Vorfahren
    inklusive der Gruppe selbst, die nicht in der Liste der echten
    Untergruppen enthalten sind.

Im Beispiel ist Uwe in den Gruppen 1 und 3 enthalten
    Vorfahren von 1 ist 1,
    Vorfahren von 3 sind 1, 2, 3
    Die Vereinigungsmenge aller Vorfahren enthält somit 1, 2, 3

Echte Untergruppen von 1 sind 2, 3, 4, 5
    Echte Untergruppen von 3 gibt es nicht

Da 2 und 3 in den echten Untergruppen enthalten sind, entfallen sie
    aus der Ausschlussliste:
    Ausgeschlossen werden nur die Mitglieder der Gruppe 1
*/

-- Die Vorfahren aller Gruppen (inklusive der Gruppen selbst)
-- eines Benutzers erhält man über

SELECT  
    ancestor.groupId  
FROM  
    groups node  
INNER JOIN  
    groups ancestor  
ON	  
    node.lKey BETWEEN ancestor.lKey AND ancestor.rKey  
WHERE  
    node.groupId IN (  
        SELECT  
            groupId  
        FROM  
            group_user gu2  
        WHERE  
            gu2.userId = ?  
    )

Liefert für den userId-Wert 1 folgendes Ergebnis

groupId
-------
    1
    1
    2
    3

-- Die Vorfahren aller Gruppen ohne die echten Untergruppen ergibt sich somit
-- aus der Kombination:

SELECT  
    ancestor.groupID  
FROM  
    groups node  
INNER JOIN  
    groups ancestor  
ON	  
    node.lKey BETWEEN ancestor.lKey AND ancestor.rKey  
WHERE  
    node.groupId IN (  
        SELECT  
            groupId  
        FROM  
            group_user gu2  
        WHERE  
            gu2.userId = ?  
    )  
AND  
    node.groupId NOT IN (  
        SELECT  
            child.groupID  
        FROM  
            groups node  
        INNER JOIN  
            groups child  
        ON  
            child.lKey > node.lKey AND child.lKey < node.rKey  
        WHERE  
            node.groupID IN (  
                SELECT  
                    groupId  
                FROM  
                    group_user gu2  
                WHERE  
                    gu2.userId = ?  
            )  
    )  

Liefert für Uwe (userId 1) folgendes Ergebnis

groupId
-------
    1

Setzen wir es zusammen:

SELECT DISTINCT   -- damit jeder Benutzer nur einmal ausgegeben wird  
    u.userId,  
    u.name  
FROM  
    user u  
INNER JOIN  
    group_user gu  
USING  
    (userId)  
WHERE  
    -- Benutzer ist in echten Untergrupen enthalten  
    gu.groupId IN (  
        SELECT  
            child.groupId  
        FROM  
            groups node  
        INNER JOIN  
            groups child  
        ON  
            child.lKey > node.lKey AND child.lKey < node.rKey  
        WHERE  
            node.groupId IN (  
                SELECT  
                    groupId  
                FROM  
                    group_user  
                WHERE  
                    userId = ?  
            )  
    )  
AND  
    -- aber nicht in übergeordneten Gruppen (inklusive dieser selbst)  
    u.userId NOT IN (  
        SELECT  
            gu2.userId  
        FROM  
            group_user gu2  
        WHERE  
            gu2.groupId IN (  
                SELECT  
                    ancestor.groupId  
                FROM  
                    groups node  
                INNER JOIN  
                    groups ancestor  
                ON	  
                    node.lKey BETWEEN ancestor.lKey AND ancestor.rKey  
                WHERE  
                    node.groupId IN (  
                        SELECT  
                            groupId  
                        FROM  
                            group_user gu2  
                        WHERE  
                            gu2.userId = ?  
                    )  
                AND  
                    node.groupId NOT IN (  
                        SELECT  
                            child.groupId  
                        FROM  
                            groups node  
                        INNER JOIN  
                            groups child  
                        ON  
                            child.lKey > node.lKey AND child.lKey < node.rKey  
                        WHERE  
                            node.groupId IN (  
                                SELECT  
                                    groupId  
                                FROM  
                                    group_user gu2  
                                WHERE  
                                    gu2.userId = ?  
                            )  
                    )  
            )  
    )  

Ergänzen wir noch die Prüfung auf die Mitgliedschaft in einer Gruppe, die für die Benutzerverwaltung berechtigt ist:

AND (  
    SELECT  
        (COUNT(groupId) > 0) Admin  
    FROM  
        group_user  
    WHERE  
        userId = ?  
    AND  
        groupId IN (  
            SELECT  
                groupId  
            FROM  
                module_group  
            WHERE  
                moduleId = 1  
        )  
)  
-- und sortieren nach Gruppe  
ORDER BY  
    gu.groupId  

Ergebisse für die userId-Wert 1 und 2 jeweils:

userId name
-----------
    3  Gabi
    4  Inge
    5  Gerd
    6  Sepp

für den userId-Wert 3:

userId name
-----------
    4  Inge

und für alle anderen ein leeres Ergebnis - wie gewünscht.

Die Idee mit implizitem Verbot bei gleichzeitigem "Verbot geht über Erlaubnis" kann zu überraschenden Ergebnissen führen: Hinzufügen zu einer weiteren Gruppe in einem anderen Ast kann effektiv zum Verlust von Berechtigungen führen, d.h. zu geringeren Rechten als vorher. Ist das so gewollt oder habe ich das System falsch verstanden?

Bitte beachte: meine Verknüpfungstabellen haben etwas andere Namen als Deine.

Freundliche Grüße

Vinzenz