Uwe: suche Hilfe bei DB-Abfrage - MySQL

Hallo!

Ich suche Hilfe bei einer Datenbankabfrage und stehe ziemlich auf dem Schlauch. Vielleicht kann mich ja jemand da runter holen :)

Zunächst hier meine Datenbank-Struktur

  
                                  ╔════════════════════╗  
                                  ║    table.groups    ║  
                                  ╠════════════════════╣  
                           ┌─────◄║         id         ║►─────┐  
                           │      ╚════════════════════╝      │  
                           │                 ▼                │  
                           │ ┌───────────────┘                │  
╔═══════════════════════╗  │ │  ╔════════════════════════╗    │  ╔════════════════════════╗  
║ table.rel_groups_user ║  │ │  ║table.rel_groups_modules║    │  ║ table.rel_groups_pages ║  
╠═══════════════════╦═══╣  │ │  ╠════════════════════╦═══╣    │  ╠════════════════════╦═══╣  
║       groupID     ║ F ║◄─┘ └─►║        groupID     ║ F ║    └─►║        groupID     ║ F ║  
╠═══════════════════╬═══╣       ╠════════════════════╬═══╣       ╠════════════════════╬═══╣  
║       userID      ║ F ║◄─┐ ┌─►║        moduleID    ║ F ║    ┌─►║        pageID      ║ F ║  
╚═══════════════════╩═══╝  │ │  ╚════════════════════╩═══╝    │  ╚════════════════════╩═══╝  
                           │ │                                │  
                           │ │                                │  
╔═══════════════════════╗  │ │  ╔════════════════════════╗    │  ╔════════════════════════╗  
║       table.user      ║  │ │  ║      table.modules     ║    │  ║      table.pages       ║  
╠═══════════════════════╣  │ │  ╠════════════════════════╣    │  ╠════════════════════════╣  
║          id           ║►─┘ └─◄║           id           ║►─┐ └─◄║           id           ║  
╚═══════════════════════╝       ╚════════════════════════╝  │    ╚════════════════════════╝  
                                                            │                  ▼  
                                                            │                  │  
                                ╔════════════════════════╗  │                  │  
                                ║table.rel_modules_pages ║  │                  │  
                                ╠════════════════════╦═══╣  │                  │  
                                ║       moduleID     ║ F ║◄─┘                  │  
                                ╠════════════════════╬═══╣                     │  
                                ║       pageID       ║ F ║◄────────────────────┘  
                                ╚════════════════════╩═══╝  

Die Felder die mit "F" gekennzeichnet sind benutzen Fremdschlüssel. Der Bezug ist durch die Pfeile dargestellt.

Nun zu meinem Problem. Gegeben sei mir eine Benutzer-ID und eine Seiten-ID.
Ich suche eine Abfrage die mir alle Gruppen-IDs des Benutzers gibt, die Seiten-Daten falls zur Gruppe gehörend (im Beispiel blöderweise auch nur die ID) und alle Module die zur Seite und zur Gruppe gehören.

Ich hoffe ich konnte mich verständlich ausdrücken.
Wie muss ich hier vorgehen? Da solche "komplizierten" Datenbankabfragen nicht unbedingt mein Fachgebiet sind, bin ich für jeden Hinweis dankbar.

MfG, Uwe

  1. hi,

    Zunächst hier meine Datenbank-Struktur

    ╔════════════════════╗
                                      ║    table.groups    ║
                                      ╠════════════════════╣
                               ┌─────◄║         id         ║►─────┐
                               │      ╚════════════════════╝      │
                               │                 ▼                │
                               │ ┌───────────────┘                │
    ╔═══════════════════════╗  │ │  ╔════════════════════════╗    │  ╔════════════════════════╗
    ║ table.rel_groups_user ║  │ │  ║table.rel_groups_modules║    │  ║ table.rel_groups_pages ║
    ╠═══════════════════╦═══╣  │ │  ╠════════════════════╦═══╣    │  ╠════════════════════╦═══╣
    ║       groupID     ║ F ║◄─┘ └─►║        groupID     ║ F ║    └─►║        groupID     ║ F ║
    ╠═══════════════════╬═══╣       ╠════════════════════╬═══╣       ╠════════════════════╬═══╣
    ║       userID      ║ F ║◄─┐ ┌─►║        moduleID    ║ F ║    ┌─►║        pageID      ║ F ║
    ╚═══════════════════╩═══╝  │ │  ╚════════════════════╩═══╝    │  ╚════════════════════╩═══╝
                               │ │                                │
                               │ │                                │
    ╔═══════════════════════╗  │ │  ╔════════════════════════╗    │  ╔════════════════════════╗
    ║       table.user      ║  │ │  ║      table.modules     ║    │  ║      table.pages       ║
    ╠═══════════════════════╣  │ │  ╠════════════════════════╣    │  ╠════════════════════════╣
    ║          id           ║►─┘ └─◄║           id           ║►─┐ └─◄║           id           ║
    ╚═══════════════════════╝       ╚════════════════════════╝  │    ╚════════════════════════╝
                                                                │                  ▼
                                                                │                  │
                                    ╔════════════════════════╗  │                  │
                                    ║table.rel_modules_pages ║  │                  │
                                    ╠════════════════════╦═══╣  │                  │
                                    ║       moduleID     ║ F ║◄─┘                  │
                                    ╠════════════════════╬═══╣                     │
                                    ║       pageID       ║ F ║◄────────────────────┘
                                    ╚════════════════════╩═══╝

      
    Meine Güte, das ist ja ein Kunswerk!!!  
      
    
    > Nun zu meinem Problem. Gegeben sei mir eine Benutzer-ID und eine Seiten-ID.  
      
    Hmm.  
      
    
    > Ich suche eine Abfrage die mir alle Gruppen-IDs des Benutzers gibt, die Seiten-Daten falls zur Gruppe gehörend (im Beispiel blöderweise auch nur die ID) und alle Module die zur Seite und zur Gruppe gehören.  
      
    Hmm. Wozu brauchst Du denn eine Zuordnung Module <=> Benutzergruppen?  
    Die Zugehörigkeit der Module zur Seite sehe ich ja noch ein ;-)
    
    1. Hallo!

      Meine Güte, das ist ja ein Kunswerk!!!

      Ich hoffe das war nicht ironisch, dann danke! Ich mag gern etwas "greifbares" wenn es um Theorie geht, vielleicht investier ich zu viel Zeit ins Design :)

      Ich habe mir lange Gedanken über die Struktur gemacht, also die des gesamten Projekts welches ich realisieren will. Falls da jemand also auch Fehler findet, bin ich auch da für Hinweise dankbar! :)

      Hmm. Wozu brauchst Du denn eine Zuordnung Module <=> Benutzergruppen?
      Die Zugehörigkeit der Module zur Seite sehe ich ja noch ein ;-)

      Weil eine Seite für verschiedene Benutzer anders aussehen kann.
      Einfaches Beispiel: verschiedene Benutzergruppen für lesen, schreiben, löschen. Alles läuft aber z.B. über das Modul "Benutzer verwalten".

      Falls du es anders lösen würdest, ich bin für jeden Vorschlag offen.

      1. hi Uwe,

        Meine Güte, das ist ja ein Kunswerk!!!

        Ich hoffe das war nicht ironisch, dann danke!

        Ne, ist ernst gemeint.

        Falls du es anders lösen würdest, ich bin für jeden Vorschlag offen.

        Ja, Deine Lösung sieht auf den ersten Blick sehr kompliziert aus, das liegt jedoch in der Natur der Sache und ist auch so.

        Lassen wir zunächst mal das benutzergruppen-Abhängige Design weg. Die Seiten-ID beschreibt ein Objekt mit Eigenschaften (Relation mit Feldern), eine davon ist die Gruppenzugehörigkeit. Es ergibt sich eine 1:n Beziehung zwischen der Tabelle mit den Seiten und der Tabelle mit den Benutzergruppen. Da hätten wir zwei Tabellen in einer Beziehung. Eine dritte Tabelle beschreibt die Beziehung zwischen Benutzer und Gruppe.

        Jetzt kommt das Design ins Spiel, die Beziehung Gruppe <=> Modul. Und die wäre 1:1 wenn ich das richtig verstanden habe. Das lässt sich in der Tabelle 'Benutzergruppen' unterbringen, ergo reichen zwei Tabellen für die erste Normalform.

        Hotti

        1. Hallo!

          Jetzt kommt das Design ins Spiel, die Beziehung Gruppe <=> Modul. Und die wäre 1:1 wenn ich das richtig verstanden habe. Das lässt sich in der Tabelle 'Benutzergruppen' unterbringen, ergo reichen zwei Tabellen für die erste Normalform.

          Die Beziehung ist/wird da leider etwas komplizierter.
          Meine Idee ist folgende: Ich lege in table.groups 3 Spalten "editable", "listable", "deletable" an und speichere darin jeweils ein serialisiertes Array mit den Rechten.

          Ist das eine schlechte Idee?

          Uwe

          1. Nachtrag:

            Die Beziehung ist/wird da leider etwas komplizierter.
            Meine Idee ist folgende: Ich lege in table.groups 3 Spalten "editable", "listable", "deletable" an und speichere darin jeweils ein serialisiertes Array mit den Rechten.

            In den serialisierten Arrays stehen die IDs der Module.

            1. Moin!

              Die Beziehung ist/wird da leider etwas komplizierter.
              Meine Idee ist folgende: Ich lege in table.groups 3 Spalten "editable", "listable", "deletable" an und speichere darin jeweils ein serialisiertes Array mit den Rechten.

              In den serialisierten Arrays stehen die IDs der Module.

              Ja, das ist schlecht. Auf diese Weise kannst du in SQL nicht mehr joinen.

              Andererseits hast du sowieso ein sehr ekliges zirkuläres Konstrukt, dass in sich problemlos inkonsistent werden kann - bzw. durch ungeschickte Dateneinträge alles ausschließt.

              - Sven Rautenberg

              1. Hallo!

                Andererseits hast du sowieso ein sehr ekliges zirkuläres Konstrukt, dass in sich problemlos inkonsistent werden kann - bzw. durch ungeschickte Dateneinträge alles ausschließt.

                Wie gesagt, ich bin für jeden Vorschlag offen. Was meinst du mit dem Satz?

                Uwe

                1. Moin!

                  Andererseits hast du sowieso ein sehr ekliges zirkuläres Konstrukt, dass in sich problemlos inkonsistent werden kann - bzw. durch ungeschickte Dateneinträge alles ausschließt.

                  Wie gesagt, ich bin für jeden Vorschlag offen. Was meinst du mit dem Satz?

                  Du hast Module, Seiten und Gruppen.

                  Zu einem Modul gehören mehrere Seiten. Aber eine Seite kann auch zu mehreren Modulen gehören.

                  Ein Modul gehört mehreren Gruppen. Und eine Gruppe hat mehrere Module.

                  Eine Seite gehört zu mehreren Gruppen. Und eine Gruppe hat mehrere Seiten.

                  • das ist mal ausformuliert das, was du bislang in deiner ASCII-Grafik modelliert hast.

                  Was ich da komisch finde:

                  • Eine Seite gehört zu mehreren Modulen? Hängt natürlich davon ab, was für dich "Seite" ist, aber ich würde das nicht so machen. Eine Seite sollte immer genau zu EINEM Modul gehören.

                  Und wenn diese Verknüpfung herausfällt, dann brauchst du auch nicht mehr die Seite den Gruppen zuordnen, sondern diese Zuordnung entsteht indirekt durch die Verknüpfung über die Module.

                  Anstatt des Kreises hast du dann nämlich eine relativ einfache Kette:

                  User <- n:m -> Gruppen <- n:m -> Module <- 1:n -> Seiten

                  Und so eine Kette ist dann deutlich einfacher abzufragen

                  Und dein Ausgangsproblem:

                  Nun zu meinem Problem. Gegeben sei mir eine Benutzer-ID und eine Seiten-ID.
                  Ich suche eine Abfrage die mir alle Gruppen-IDs des Benutzers gibt, die Seiten-Daten falls zur Gruppe gehörend (im Beispiel blöderweise auch nur die ID) und alle Module die zur Seite und zur Gruppe gehören.

                  Grundsätzlich würde ich die einem User zugeordneten Gruppen immer mit in die Session ziehen, denn Zugriffsrechte orientieren sich an der Gruppe, nicht am User.

                  Und selbst wenn das nicht geht: Die Benutzer-ID steht in der n:m-Tabelle zwischen User und Gruppen, die Seiten-ID steht in der Seitentabelle - beide Tabellen enthalten als Verknüpfungspunkt die Gruppen-ID bzw. die Modul-ID, welche beide ihrerseits in der n:M-Tabelle zwischen Gruppen und Modulen verknüpft sind.

                  Du fragst also im Prinzip erstmal nur die verbindenden Tabellen ab. Nur wenn dich zu einer ID auch die Details interessieren, kannst du die zugehörige Tabelle am Ende auch noch hineinjoinen und abfragen.

                  - Sven Rautenberg

                  1. Hallo!

                    Was ich da komisch finde:

                    • Eine Seite gehört zu mehreren Modulen? Hängt natürlich davon ab, was für dich "Seite" ist, aber ich würde das nicht so machen. Eine Seite sollte immer genau zu EINEM Modul gehören.

                    Wieso sollte das so sein? Ich könnte z.B. eine Seite haben auf der die Module "Login" und "News" sind. Auf der nächsten Seite habe ich dann "Login" und "Artikel" usw. Deshalb diese Tabellen-Verknüpfung. Ist das denn so schlecht? Wie sollte ich die beiden Module als 1 Modul in die Seite fügen?

                    Und dein Ausgangsproblem:
                    Grundsätzlich würde ich die einem User zugeordneten Gruppen immer mit in die Session ziehen, denn Zugriffsrechte orientieren sich an der Gruppe, nicht am User.

                    Was meinst du mit "mit in die Session ziehen"? Ansonsten ist das auch der Plan, dass die Zugriffsrechts von den Gruppen bezogen werden.

                    Du fragst also im Prinzip erstmal nur die verbindenden Tabellen ab. Nur wenn dich zu einer ID auch die Details interessieren, kannst du die zugehörige Tabelle am Ende auch noch hineinjoinen und abfragen.

                    Also fange ich bei table.rel_groups_user mit der Abfrage an dann ein join in table.groups. Aber wie frage ich ab ob ein Modul für die Gruppe(n) und Seite zur Verfügung steht. Ich weiß nicht wie ich die Tabelle table.rel_modules_pages weglassen kann. Siehe oben.

                    Um nochmal auf die Frage zurück zu kommen, wieso können die Tabellen inkonsistent werden bzw. wie wird durch welche ungeschickten Datensätze was ausgeschlossen?

                    Danke für deine Hilfe!

                    Uwe

                    1. Moin!

                      Was ich da komisch finde:

                      • Eine Seite gehört zu mehreren Modulen? Hängt natürlich davon ab, was für dich "Seite" ist, aber ich würde das nicht so machen. Eine Seite sollte immer genau zu EINEM Modul gehören.

                      Wieso sollte das so sein? Ich könnte z.B. eine Seite haben auf der die Module "Login" und "News" sind. Auf der nächsten Seite habe ich dann "Login" und "Artikel" usw. Deshalb diese Tabellen-Verknüpfung. Ist das denn so schlecht? Wie sollte ich die beiden Module als 1 Modul in die Seite fügen?

                      Ok, wenn du das so herum siehst, dann gehören eben mehrere Module zu einer Seite, und die Seite ist das bestimmende Element.

                      Was nicht sein sollte: Dass ein Modul mehrere Seiten hat, und auf jeder Seite wieder mehrere Module, die wieder mehrere Seiten ... Du erkennst das Problem? Deine Struktur erlaubt beliebig tiefes verschachteln von Modulen mit Seiten mit Modulen mit Seiten. Und nur durch die Tatsache, dass "Seite" ein Element ist, was sich in der Realität nicht selbst enthalten kann, wird die Sinnlosigkeit dieser Strukturmöglichkeit erst deutlich.

                      Wenn du Obermodule für mehrere Seiten und Untermodule als Element auf einer Seite haben willst, dann benenne nicht beides "Module" und verwalte nicht beides in derselben Tabelle.

                      Grundsätzlich würde ich die einem User zugeordneten Gruppen immer mit in die Session ziehen, denn Zugriffsrechte orientieren sich an der Gruppe, nicht am User.

                      Was meinst du mit "mit in die Session ziehen"? Ansonsten ist das auch der Plan, dass die Zugriffsrechts von den Gruppen bezogen werden.

                      Wenn ein User sich angemeldet hat, dann kennst du seine User-ID in der Session. Schreib seine Gruppen auch noch hinein, das vereinfach dir die Prüfung der Zugriffsrechte.

                      Also fange ich bei table.rel_groups_user mit der Abfrage an dann ein join in table.groups.

                      Nein, die Gruppentabelle bringt dich ja nicht weiter, die ist ja der Endpunkt für die Gruppen-ID.

                      Du verknüpfst direkt mit table.groups_module und kriegst dann Modul-IDs - und von dort geht es zu den Seiten.

                      Wie erwähnt: Die Endtabellen, in denen die Foreign-IDs definiert werden, joinst du nur hinzu, wenn dich die Detailinfos zu der jeweiligen ID auch interessieren.

                      Aber wie frage ich ab ob ein Modul für die Gruppe(n) und Seite zur Verfügung steht. Ich weiß nicht wie ich die Tabelle table.rel_modules_pages weglassen kann. Siehe oben.

                      Wenn du strukturierst, dass Module und Seiten nur eine 1:n-Beziehung haben, dann entfällt diese Tabelle, und du fügst der Seitentabelle eine Spalte "modul-id" hinzu.

                      Das ist die klassische Vorgehensweise für 1:n-Beziehungen. :)

                      Um nochmal auf die Frage zurück zu kommen, wieso können die Tabellen inkonsistent werden bzw. wie wird durch welche ungeschickten Datensätze was ausgeschlossen?

                      Du kannst sagen: Zur Gruppe 1 gehört Modul 1, und zur Gruppe 1 gehört Seite 1. Dir fehlt jetzt aber die Verknüpfung "Seite 1 gehört zu Modul 1".

                      Wenn du die Berechtigung durchgehst:
                      Ein User der Gruppe 1 will die Seite 1 ansehen: Darf er das? Was sieht er da? Modul 1? Das gehört zu seiner Gruppe, aber nicht zur Seite 1.

                      Außerdem kann man definieren: Zur Gruppe 2 gehört Modul 2, und zu Modul 2 gehört Seite 2.

                      Ein User der Gruppe 2 will Seite 2 ansehen. Er darf Modul 2 sehen, und Modul 2 verknüpft Seite 2 - aber Gruppe 2 hat keine direkte Verbindung zu Seite 2. Ist das jetzt erlaubt?

                      Die dritte Definitionsmöglichkeit: Zur Gruppe 3 gehört Seite 3, und zur Seite 3 gehört Modul 3.

                      Ein User der Gruppe 3 will Seite 3 ansehen. Darf er das, weil Seite 3 zwar Modul 3 verbindet, aber Modul 3 nicht zu Gruppe 3 gehört?

                      Wie du siehst, erlaubt dein "Ring" bzw. Dreieck der Tabellenverknüpfung, dass zwar ein logischer Zusammenhang zwischen den drei Tabellen Gruppe, Seite und Modul hergestellt wird, aber man kann jede der drei Dreiecks-Seiten offenlassen, und erhält dann schwierig zu beantwortende Fragen, wie solch ein "offener Ring" zu interpretieren ist. Die fehlende Verknüpfung ist jeweils das Problem: Wie ist sie zu schließen.

                      Außerdem sind überzählige Verknüpfungen das Problem: So ein Ring hat eben nicht automatisch nur eine einzige logische Verknüpfung, die den Ring schließt, sondern rein von den möglichen Werten in der DB ist jede beliebige Verknüpfung möglich: Du kannst (Fall 1) eben auch von Modul 1 eine Verknüpfung zu Seite 3 machen. Welche Auswirkung hat das?

                      Wenn du tatsächlich auf der gemeinsamen Verknüpfung von Seiten, Modulen und Gruppen bestehst, dann verknüpfe alle drei Werte nur in einer einzigen Tabelle!

                      Dann siehst du auch sofort offensichtliche Definitionslücken, bzw. kannst durch NULL-Spalten sowas wie Joker (Gilt für alles) implementieren.

                      Nur kurz Skizziert: Das Loginmodul 1 soll auf allen Seiten (*) erscheinen, aber nur für die Gruppe nichtangemeldeter User (1):

                      modul - seite - gruppe
                      1       NULL    1

                      Das Wetter-Modul (2) soll auf allen Seiten erscheinen, aber nur für reguläre User (Gruppe 2) und Admins (Gruppe 3)
                      2       NULL    2
                      2       NULL    3

                      Das IP-Info-Modul (3) soll nur auf der Netzwerkseite (1) erscheinen, aber für alle Gruppen:
                      3       1       NULL

                      Alternativ, weil die Möglichkeit "alle Gruppen" eventuell zu hohes Risiko bedeutet, müsstest du für jede Gruppe einen Eintrag machen:
                      3       1       1
                      3       1       2
                      3       1       3

                      Du ersetzt deine drei Tabellen, die jeweils nur zwei Tabellen verknüpfen, also durch eine einzige Tabelle, die drei Tabellen verknüpft. Und ein Eintrag in dieser Tabelle ist automatisch konsistent, weil er eindeutig alle drei Tabellen verknüpft (oder durch NULL eben "alles" gemeint sein kann, wenn die Applikation das so will).

                      Bedenke, dass auf die drei Spalten zwar ein übergreifender Unique-Key gesetzt werden kann, bei NULL-fähigen Spalten darin aber keine Uniqueness mehr garantiert wird in MySQL - works as designed im Hinblick auf die Bedeutung von NULL. NULL != NULL für alle Werte von NULL, deshalb ist das Tupen "1-2-NULL" ungleich "1-2-NULL" im unique index.

                      - Sven Rautenberg

                      1. Hallo!

                        Wow danke! Jetzt hab ich das Problem mit dem Ring endlich verstanden.
                        Auch die Tabelle mit 3 Fremdschlüsseln gefällt mir auf den ersten Blick sehr gut.

                        Ich werde also meine Datenbank mal umstrukturieren und schaun wie weit ich damit komme.

                        Soweit also erstmal herzlichen Dank für deine geduldige Aufklärungsarbeit!

                        Uwe

                      2. Hallo nochmal!

                        Und da kommt auch schon die erste Rückfrage... :)
                        Wie handhabe ich den Fall wenn ich einem Benutzer eine Seite unabhängig seiner Benutzergruppen zuordnen will? Erstell ich ihm dazu eine eigene Gruppe der ich keine anderen Benutzer zuweisen kann? Z.B. eine Spalte "owner" in table.groups in der die ID des Benutzers steht und NULL falls die Gruppe eine "normale Gruppe" ist?

                        Uwe

                        1. Moin!

                          Und da kommt auch schon die erste Rückfrage... :)
                          Wie handhabe ich den Fall wenn ich einem Benutzer eine Seite unabhängig seiner Benutzergruppen zuordnen will? Erstell ich ihm dazu eine eigene Gruppe der ich keine anderen Benutzer zuweisen kann? Z.B. eine Spalte "owner" in table.groups in der die ID des Benutzers steht und NULL falls die Gruppe eine "normale Gruppe" ist?

                          User sind ausschließlich Gruppen zugeordnet, nix anderem. Und für die Gruppen gibts dann die Rechte zum Zugriff auf alles andere.

                          Folglich ist es dann, wenn dein System steht und tatsächlich ein einzelner User Zugriff auf eine spezielle Seite haben soll, vom Admin her notwendig, dem User eine Einzelgruppe zuzuweisen und dieser dann die Seite.

                          Sowas modelliert man aber nicht in der Datenbank. Ich würde auch meinen, dass sowas eher ein Einzelfall ist, der dann zwar etwas mehr manuelle Verwaltungsarbeit erfordert, aber mit dem existierenden System ja erschlagen werden kann.

                          - Sven Rautenberg

                          1. Hallo und Danke nochmals!

                            Jetzt hab ich also folgende DB-Struktur:

                            ╔════════════════════╗
                                                              ║    table.groups    ║
                                                              ╠════════════════════╣
                                                       ┌─────◄║         id         ║
                                                       │      ╚════════════════════╝
                                                       │                 ▼
                                                       │                 └────────────────────┐
                            ╔═══════════════════════╗  │    ╔══════════════════════════════╗  │   ╔════════════════════════╗
                            ║ table.rel_groups_user ║  │    ║table.rel_groups_modules_pages║  │   ║     table.modules      ║
                            ╠═══════════════════╦═══╣  │    ╠══════════════════════════╦═══╣  │   ╠════════════════════════╣
                            ║       groupID     ║ F ║◄─┘    ║        groupID           ║ F ║►─┘┌─◄║           id           ║
                            ╠═══════════════════╬═══╣       ╠══════════════════════════╬═══╣   │  ╚════════════════════════╝
                            ║       userID      ║ F ║◄─┐    ║        moduleID          ║ F ║►──┘
                            ╚═══════════════════╩═══╝  │    ╠══════════════════════════╬═══╣
                                                       │    ║        pageID            ║ F ║►──┐  ╔════════════════════════╗
                                                       │    ╚══════════════════════════╩═══╝   │  ║      table.pages       ║
                            ╔═══════════════════════╗  │                                       │  ╠════════════════════════╣
                            ║       table.user      ║  │                                       └─◄║           id           ║
                            ╠═══════════════════════╣  │                                          ╚════════════════════════╝
                            ║          id           ║►─┘
                            ╚═══════════════════════╝

                            Soweit so gut?

                            @dedlfix: Das "Anwendungsgebiet"... Es soll am Ende ein CMS werden :)

                            Uwe

                            1. Hallo!

                              Ich habe mich dann also mit den neuen Tabellen an die Abfrage gewagt.

                                
                              SELECT  
                                  `groups`.id AS groupID,  
                                  `pages`.id AS pageID,  
                                  `modules`.id AS moduleID  
                              FROM  
                                  `rel_groups_user`  
                              LEFT JOIN  
                                  `groups`  
                              ON  
                                  `groups`.id = `rel_groups_user`.groupID  
                              LEFT JOIN  
                                  `rel_groups_modules_pages`  
                              ON  
                                  `rel_groups_modules_pages`.groupID = `groups`.id  
                              LEFT JOIN  
                                  `modules`  
                              ON  
                                  `modules`.id = `rel_groups_modules_pages`.moduleID  
                              LEFT JOIN  
                                  `pages`  
                              ON  
                                  `pages`.id = `rel_groups_modules_pages`.pageID  
                              WHERE  
                                  `rel_groups_user`.userID = ?  
                              AND  
                                  `rel_groups_modules_pages`.pageID = ?
                              

                              Wenn ich es richtig sehe, bekomme ich damit alle Daten die ich wollte. Ich müsste hinterher nur noch etwas sortieren.
                              Was haltet ihr davon? Ist die Abfrage ok oder geht es besser/schneller/unkomplizierter?

                              Uwe

                              1. Moin!

                                Ich habe mich dann also mit den neuen Tabellen an die Abfrage gewagt.

                                Geht es dir wirklich nur um die IDs?

                                Dann kannst du dir sämtliche JOINs auf die Tabellen groups, pages und modules sparen, denn die IDs stehen auch in rel_groups_modules_pages alle drin.

                                Schlimmstenfalls brauchts noch ein JOIN auf rel_groups_user.

                                Und WENN, dann würde ich tatsächlich erstmal den primär die Daten sachlich verknüpfenden JOIN ganz an den Anfang bringen, nämlich rel_groups_modules_pages und rel_groups_user.

                                Noch ein guter JOIN-Rat: Ich mag die Prämisse "Gleiches muss gleich heißen", deshalb heißt bei mir in allen Tabellen die userID immer userID, auch in der Tabelle user.userID. Der Vorteil: Ich kann die JOIN-Bedingung mittels "USING (userID)" formulieren und muss nicht "user.ID = rel_groups_user.userID" schreiben.

                                Gleiches heißt gleich und ist verknüpft - das ist ein super Namensschema.

                                Und du sparst dir dann auch die dummen Alias-Bezeichner für alle diese Fälle, dass du aus mehr als einer Tabelle mal deren ID wissen willst, weil diese ID schon automatisch den Tabellennamen, aus dem sie ursprünglich kommt, in sich trägt.

                                  
                                SELECT  
                                    `groups`.groupID,  
                                    `pages`.pageID,  
                                    `modules`.moduleID  
                                FROM  
                                    `rel_groups_user`  
                                LEFT JOIN  
                                    `rel_groups_modules_pages` USING (groupID)  
                                  
                                LEFT JOIN  
                                    `groups` USING (groupID)  
                                LEFT JOIN  
                                    `modules` USING (moduleID)  
                                LEFT JOIN  
                                    `pages` USING (pageID)  
                                  
                                WHERE  
                                     userID = ?  
                                AND  
                                    pageID = ?
                                

                                Wenn ich es richtig sehe, bekomme ich damit alle Daten die ich wollte. Ich müsste hinterher nur noch etwas sortieren.
                                Was haltet ihr davon? Ist die Abfrage ok oder geht es besser/schneller/unkomplizierter?

                                Mit USING wirds auf jeden Fall übersichtlicher, sowas geht aber nur bei namensgleichen Spalten in den beiden Tabellen.

                                Und alles andere, insbesondere Performance, sollte an dieser Stelle noch nicht bewertet werden. Logisch, dass du in der Tabellenstruktur mit passenden Indices arbeiten wirst, aber ich muss dir sagen, dass ich persönlich mir zum Erstellzeitpunkt der Tabelle über nützliche Index-Definition noch keine großen Gedanken mache, bzw. dieses Thema auf später verschiebe. Erst wenn tatsächlich ein paar Daten in der DB existieren und die abfragenden Querys sich nicht mehr groß ändern, kann man mal ein EXPLAIN laufen lassen, um dann die notwendigen Indices zu definieren. Sollte man aber natürlich auch nicht vergessen. :)

                                - Sven Rautenberg

                                1. Hallo!

                                  Geht es dir wirklich nur um die IDs?

                                  Nein, keinesfalls. In sämtlichen Tabellen (ausser die mit den Beziehungen) stehen z.B. noch Namen, Alias-Namen, Status usw.

                                  Und WENN, dann würde ich tatsächlich erstmal den primär die Daten sachlich verknüpfenden JOIN ganz an den Anfang bringen, nämlich rel_groups_modules_pages und rel_groups_user.

                                  Noch ein guter JOIN-Rat: Ich mag die Prämisse "Gleiches muss gleich heißen", deshalb heißt bei mir in allen Tabellen die userID immer userID, auch in der Tabelle user.userID. Der Vorteil: Ich kann die JOIN-Bedingung mittels "USING (userID)" formulieren und muss nicht "user.ID = rel_groups_user.userID" schreiben.

                                  Gleiches heißt gleich und ist verknüpft - das ist ein super Namensschema.

                                  Und du sparst dir dann auch die dummen Alias-Bezeichner für alle diese Fälle, dass du aus mehr als einer Tabelle mal deren ID wissen willst, weil diese ID schon automatisch den Tabellennamen, aus dem sie ursprünglich kommt, in sich trägt.

                                  Klingt alles sehr vernünftig. Ich werde das morgen entsprechend "anpassen" und testen. Für heute reichts dann auch und danke für den Hinweis auf USING :)

                                  Uwe

                      3. Hallo!

                        Wenn ein User sich angemeldet hat, dann kennst du seine User-ID in der Session. Schreib seine Gruppen auch noch hinein, das vereinfach dir die Prüfung der Zugriffsrechte.

                        Dazu hätte ich noch eine Frage. Inwiefern vereinfacht mir das die Prüfung?
                        Die Session könnte doch immer potentiell manipuliert sein (Client->Server) also bleibt mir doch nichts übrig als "nur" zu prüfen ob Benutzer-ID und Session-ID bereits bekannt sind (sprich in der DB gespeichert).
                        Muss ich alles andere nicht sowieso abfragen?

                        Uwe

                        1. Moin!

                          Wenn ein User sich angemeldet hat, dann kennst du seine User-ID in der Session. Schreib seine Gruppen auch noch hinein, das vereinfach dir die Prüfung der Zugriffsrechte.

                          Dazu hätte ich noch eine Frage. Inwiefern vereinfacht mir das die Prüfung?

                          Weil du zur Gruppenzugehörigkeit des Users nicht immer wieder die DB befragen musst. Oder alternativ die Gruppenzugehörigkeit standardmäßig bei jedem Request sowieso aus der DB ausliest und deshalb verfügbar hast.

                          Die Session könnte doch immer potentiell manipuliert sein (Client->Server) also bleibt mir doch nichts übrig als "nur" zu prüfen ob Benutzer-ID und Session-ID bereits bekannt sind (sprich in der DB gespeichert).

                          Die Session-Info in $_SESSION kann nicht manipuliert sein. Wenn ein Angreifer in der Lage ist, die Session-ID einer aktiven Session zu raten, hast du verloren, egal was du machst, denn nur die Unratbarkeit dieser ID gewährleistet die Sicherheit.

                          In der Session stehen also Dinge über den zugehörigen Benutzer: Bis wann ist die Session gültig? Ist er angemeldet und als wer (User-ID)? Welchen Usergruppen gehört er an, also welche Rechte hat er?

                          Muss ich alles andere nicht sowieso abfragen?

                          Du kannst natürlich die Infos zu dem jeweiligen User bzw. den Gruppenrechten bei jedem Request abfragen. Das verhindert, dass sich ein User mal irgendwann einloggt, die Session unendlich lange aktiv hält, und du ihn administrativ nicht sperren kannst, weil das Ändern seiner Rechte in der DB nie erneut aus der DB gelesen wird, sondern immer noch der Zustand von "anno" in $_SESSION steht.

                          Ich würde nur vermeiden wollen, aus der DB anfangs wirklich nur den User auszulesen, und später über die Konstruktion von Gruppen, Modulen und Seiten auch noch einen JOIN hin zu den UserIDs ausführen zu müssen. Ich habe dafür keine harten Belege, aber meine gesammelten Erfahrungswerte sagen mir, dass man die Gruppenzugehörigkeit eines Users an viel mehr Stellen "direkt" brauchen kann, und dass deshalb diese Info direkt mit zum User gehört.

                          - Sven Rautenberg

                          1. Hallo!

                            Du kannst natürlich die Infos zu dem jeweiligen User bzw. den Gruppenrechten bei jedem Request abfragen. Das verhindert, dass sich ein User mal irgendwann einloggt, die Session unendlich lange aktiv hält, und du ihn administrativ nicht sperren kannst, weil das Ändern seiner Rechte in der DB nie erneut aus der DB gelesen wird, sondern immer noch der Zustand von "anno" in $_SESSION steht.

                            Darum geht es mir ja. Eine Session sollte ab einem bestimmten "timeout" schon "automatisch" geschlossen werden. Ich dachte mir, dass das Sicherheitstechnisch eine gute Lösung wäre.

                            Ich würde nur vermeiden wollen, aus der DB anfangs wirklich nur den User auszulesen, und später über die Konstruktion von Gruppen, Modulen und Seiten auch noch einen JOIN hin zu den UserIDs ausführen zu müssen. Ich habe dafür keine harten Belege, aber meine gesammelten Erfahrungswerte sagen mir, dass man die Gruppenzugehörigkeit eines Users an viel mehr Stellen "direkt" brauchen kann, und dass deshalb diese Info direkt mit zum User gehört.

                            Ja klar, ich möchte die Benutzerdaten auch so zeitig wie möglich erfahren um sie z.b. auch den Modulen zur Verfügung zu stellen. Trotzdem muss ich doch aber eine Gegenprüfung bzgl. der Benutzergruppen/-rechte durchführen. Oder nicht? Ansonsten könnte der Client seinem Cookie doch z.B. einfach eine Gruppe hinzufügen. Das hat dann nichtmal was mit Session-Diebstahl zu tun. Oder versteh ich da was falsch?

                            Uwe

                            1. Hi!

                              Ja klar, ich möchte die Benutzerdaten auch so zeitig wie möglich erfahren um sie z.b. auch den Modulen zur Verfügung zu stellen.

                              So zeitig wie möglich ist nicht notwendig. So zeitig wie nötig wäre auch ausreichend. Dann

                              Trotzdem muss ich doch aber eine Gegenprüfung bzgl. der Benutzergruppen/-rechte durchführen. Oder nicht? Ansonsten könnte der Client seinem Cookie doch z.B. einfach eine Gruppe hinzufügen. Das hat dann nichtmal was mit Session-Diebstahl zu tun. Oder versteh ich da was falsch?

                              Ja. Öffne eine Session, schreib ein paar Werte hinein. Mach einen zweiten Request, schau nach, ob die Werte noch da sind. Schau dir nun im Browser die Cookies an. Was siehst du? Nur die Session-ID. Wenn man nicht nur die Session-ID sondern auch alle Daten ständig zwischen Client und Server hin- und hersendete, bräuchte man den Session-Mechanismus nicht, das ginge mit einfachem GET/POST/COOKIE, wobei GET und Cookies den Nachteil der De-Facto-Größenbeschränkung haben.

                              Session-Daten liegen immer auf dem Server. Aber auch da kannst du nicht die Hände in den Schoß legen sondern solltest dich auch dort mit den möglichen Risiken vertraut machen. Beispielsweise wenn alle Anwendungen nur ein gemeinsames Session-Verzeichnis nutzen, dann können sich die Garbage-Collectors der Anwendungen gegenseitig die Session-Dateien löschen, wenn diese (GCs) unterschiedliche Ablaufzeiten eingestellt haben. Ein separates Verzeichnis für Session-Daten verhindert dies. Dies nutzen zu können setzt aber voraus, dass einem der Hoster Platz außerhalb des DocumentRoots zur Verfügung stellt oder sich die DocumentRoots in Unterverzeichnisse des Kundenverzeichnisses legen lassen und man dann "daneben" Platz hat.

                              Lo!

                              1. Hallo!

                                Trotzdem muss ich doch aber eine Gegenprüfung bzgl. der Benutzergruppen/-rechte durchführen. Oder nicht? Ansonsten könnte der Client seinem Cookie doch z.B. einfach eine Gruppe hinzufügen. Das hat dann nichtmal was mit Session-Diebstahl zu tun. Oder versteh ich da was falsch?

                                Ja. Öffne eine Session, schreib ein paar Werte hinein. Mach einen zweiten Request, schau nach, ob die Werte noch da sind. Schau dir nun im Browser die Cookies an. Was siehst du? Nur die Session-ID. Wenn man nicht nur die Session-ID sondern auch alle Daten ständig zwischen Client und Server hin- und hersendete, bräuchte man den Session-Mechanismus nicht, das ginge mit einfachem GET/POST/COOKIE, wobei GET und Cookies den Nachteil der De-Facto-Größenbeschränkung haben.

                                Sorry, ich steh schon wieder auf dem Schlauch :(
                                Was bedeutet das jetzt für mich? Also kommen die Gruppendaten in die Session und die Session-ID wird dann, je nach Einstellung, als Cookie übertragen, die Gruppendaten stehen aber nicht dort drin und sind deshalb auch nicht manipulierbar?

                                Entschuldigung, dass ich grad ein Brett vor dem Kopf habe aber mir brummt der Schädel :)

                                Uwe

                                1. Hi!

                                  Also kommen die Gruppendaten in die Session und die Session-ID wird dann, je nach Einstellung, als Cookie übertragen, die Gruppendaten stehen aber nicht dort drin und sind deshalb auch nicht manipulierbar?

                                  Genau. Session-Daten bleiben auf dem Server und nur die Session-ID wird dem Client mitgeteilt, der diese bei seinen Requests wieder mitsendet und somit erkennbar ist. Der Server öffnet die der Session-ID zugeordnete Datenhaltung und die Daten stehen wieder zur Verfügung. PHP legt dafür eine Datei pro Session im mit session.save_path konfigurierten Verzeichnis an. Aber auch eine Datenbank ist als Ablage konfigurierbar (braucht man, wenn man Lastverteilung über mehrere Server haben möchte und die Session-Daten zentral verfügbar sein müssen).

                                  Lo!

        2. Hi!

          Die Seiten-ID beschreibt ein Objekt mit Eigenschaften (Relation mit Feldern), eine davon ist die Gruppenzugehörigkeit. Es ergibt sich eine 1:n Beziehung zwischen der Tabelle mit den Seiten und der Tabelle mit den Benutzergruppen.

          Nein, das ist eine m:n-Beziehung, die über die Hilfstabelle rel_groups_pages realisiert wird. Seiten können mehreren Gruppen zugeodnet sein und Gruppen können auch noch Beziehungen zu anderen Seiten haben.

          Da hätten wir zwei Tabellen in einer Beziehung. Eine dritte Tabelle beschreibt die Beziehung zwischen Benutzer und Gruppe.

          Auch falsch. Die drei Tabellen pages, groups und rel_groups_pages beschreiben die eine Beziehung zwischen Seiten und Gruppen.

          Jetzt kommt das Design ins Spiel, die Beziehung Gruppe <=> Modul. Und die wäre 1:1 wenn ich das richtig verstanden habe.

          Das ist auch m:n. m:n-Beziehungen sind immer über die Hilfstabelle zu erkennen. 1:1 und 1:n kommen ohne Hilfstabellen aus.

          Ob allerdings die gezeigte Tabellenstruktur mit der Wirklichkeit übereinstimmt oder nur fälschlicherweise m:n-Tabellen für eine eigentliche 1:1- oder 1:n-Beziehung verwendet wurden, geht aus den bisherigen Aussagen nicht hervor. Und nach meinen Vorstellungen zur Zielsetzung des OPs sehe ich auch nicht, dass keine m:n-Beziehungen gefragt sind.

          Lo!

          1. Hallo!

            Ob allerdings die gezeigte Tabellenstruktur mit der Wirklichkeit übereinstimmt oder nur fälschlicherweise m:n-Tabellen für eine eigentliche 1:1- oder 1:n-Beziehung verwendet wurden, geht aus den bisherigen Aussagen nicht hervor. Und nach meinen Vorstellungen zur Zielsetzung des OPs sehe ich auch nicht, dass keine m:n-Beziehungen gefragt sind.

            Die Tabellenstruktur befindet sich zur Zeit genau so in meiner lokalen Datenbank. Die Tabellen (ausser die mit den Beziehungen) haben zwar noch weitere Spalten, die sollten aber hier nichts zur Sache tun.

            Ich könnte in der Tabelle table.user bereits die Gruppenzuordnungen speichern, das wollte ich aber aufgrund von steigender Redundanz nicht.
            Die beiden anderen m:n-Tabellen lassen sich imho nicht vermeiden.

            Ich möchte an der Stelle gern anmerken, dass ich auch durchaus dazu bereit bin die komplette Tabellenstruktur über den Haufen zu werfen und mit einer "optimierten" Lösung von vorn zu beginnen.

            Uwe

            1. Hi!

              Ich könnte in der Tabelle table.user bereits die Gruppenzuordnungen speichern, das wollte ich aber aufgrund von steigender Redundanz nicht.

              Das geht ja nur, wenn du diese Information komma(o.ä.)getrennt in einem Feld ablegst oder der Anwender nur einer Gruppe zugeordnet sein darf. Mehrere Werte in einem Feld sind immer dann schlecht, wenn die einzelnen Informationen in diesem Feld für verknüpfte Abfragen verwendet werden sollen. Wenn das nicht geplant ist, spricht eigentlich nichts gegen eine Zusammenfassung in einem Feld. Aber meistens kommt es anders als gedacht und für ein neues Feature braucht man die Daten einzeln - und dann steht man da. Also lieber gleich ordentlich aufsetzen.

              Lo!

  2. Hi!

    Zunächst hier meine Datenbank-Struktur
    [code lang=php]

    Was ist denn an diesem Diagramm PHP - was ist daran überhaupt Code?

    Nun zu meinem Problem. Gegeben sei mir eine Benutzer-ID und eine Seiten-ID.
    Ich suche eine Abfrage die mir alle Gruppen-IDs des Benutzers gibt, die Seiten-Daten falls zur Gruppe gehörend (im Beispiel blöderweise auch nur die ID) und alle Module die zur Seite und zur Gruppe gehören.

    Ich hoffe ich konnte mich verständlich ausdrücken.

    Noch nicht. Welche Daten willst du hauptsächlich haben und was ist das Zubehör? Beispieldaten und die des gewünschten Ergebnisses wären hilfreich. Wenn A in den Gruppen 1 und 2 ist, sowie B in 3 und 4, dann bekommst du bei deinem Wunsch

    A 1
    A 2
    B 3
    B 4

    also jeweils zweimal den Benutzer. Üblicherweise möchte man das der Übersichtlichkeit eher als Master-Detail darstellen, also

    A
      1
      2
    B
      3
      4

    Bei obigem Join bekommst du redundant immer wieder die Daten von A respektive B. Und das wird nicht besser, wenn du da noch mehr hinzujoinst. Also was konkret willst du anzeigen/erhalten?

    Wie muss ich hier vorgehen? Da solche "komplizierten" Datenbankabfragen nicht unbedingt mein Fachgebiet sind, bin ich für jeden Hinweis dankbar.

    Joins - einfach und fortgeschritten - wären allgemeine Informationen.

    Lo!

    1. Hallo!

      Was ist denn an diesem Diagramm PHP - was ist daran überhaupt Code?

      Nichts, ich wusste nur nicht wie ich es besser darstellen kann aufgrund der "verschluckten" Leerzeichen nei normaler Darstellung im HTML.
      Wie hätte ich es machen sollen?

      Noch nicht. Welche Daten willst du hauptsächlich haben und was ist das Zubehör? Beispieldaten und die des gewünschten Ergebnisses wären hilfreich.

      Was meinst du mit Zubehör? Beispieldaten wären einfach verschiedene ids.

      Bei obigem Join bekommst du redundant immer wieder die Daten von A respektive B. Und das wird nicht besser, wenn du da noch mehr hinzujoinst. Also was konkret willst du anzeigen/erhalten?

      Haben will ich "am Ende" ein Array/Objekt, dass mir Informationen über den Seiteninhalt entsprechend dem User gibt. Davon abhängig ist in welchen Gruppen er ist, welche Module und welche Seiten für diese Gruppe existieren.
      Ich möchte also die Daten der Gruppe (darin enthalten sind Informationen über Rechte der Module; lesen/schreiben/löschen), der Module und der Seite. Die ID des Users dient nur als Suchkriterium.

      Am Ende hätte ich gern ein Page-Objekt:

      object  
      {  
          // table.pages  
          page array('pageID'     => $pageID,                                // Alias für table.pages.id  
                     'pageData1'  => $pageData1,                             // Alias für Spalten  
                     'pageData2'  => $pageData2,  
                     /*..*/)  
        
          // table.modules  
          modules array(moduleID1 => array('modulData1_1' => $modulData1_1,  // moduleID1 = Alias für table.modules.id  
                                          'modulData1_2' => $modulData1_2),  // modulData1_1, 1_2,.. = Alias für Spalten  
                        moduleID2 => array('modulData2_1' => $modulData2_1,  
                                           /*..*/)),  
        
          // table.groups  
          groups array(groupsID1  => array('groupData1_1' => $groupData1_1,   // groupsID1 = Alias für table.groups.id  
                                           'groupData1_2' => $groupData1_2,   // groupData1_1, 1_2,.. = Alias für Spalten  
                                           'groupData1_3' => $groupData1_3,   // aus table.groups  
                                           /*..*/),  
                       groupsID2  => array(groupData2_1 => groupData2_1,  
                                           /*..*/))  
      }  
      
      

      Somit habe ich die Daten aus der Gruppe passend zu jedem Modul einer bestimmten Seite und entsprechend einer Benutzer-ID.

      Joins - einfach und fortgeschritten - wären allgemeine Informationen.

      Da hab ich schon geschaut, ich hab auch Ansätze, zum Schluss endet aber alles in einem Gewirr aus LEFT JOINs und spätestens beim Versuch die Module passend zur Benutzergruppe und Seite zu selektieren scheitere ich völlig.
      Mittlerweile bin ich so verwirrt, dass ich nicht mehr weiß ob ich anfangen soll in der Tabelle table.user oder table.groups zu suchen. Ich glaub table.groups passt schon - ich bin echt verwirrt. :(

      Uwe

      1. Hi!

        Was ist denn an diesem Diagramm PHP - was ist daran überhaupt Code?
        Nichts, ich wusste nur nicht wie ich es besser darstellen kann aufgrund der "verschluckten" Leerzeichen nei normaler Darstellung im HTML.
        Wie hätte ich es machen sollen?

        Einfach einstellen. Die Forumssoftware darf Code auch dann nicht verhauen, wenn er nicht als solcher ausgezeichnet ist, und sie macht das auch nicht. Siehe mein zweites Verknüpfungsbeispiel. <code> ist auch normales HTML, und normalem HTML kann man mit CSS beibringen, Whitespace-Zeichen nicht zusammenzufassen.

        Welche Daten willst du hauptsächlich haben und was ist das Zubehör? Beispieldaten und die des gewünschten Ergebnisses wären hilfreich.
        Was meinst du mit Zubehör? Beispieldaten wären einfach verschiedene ids.
        Haben will ich "am Ende" ein Array/Objekt, dass mir Informationen über den Seiteninhalt entsprechend dem User gibt. Davon abhängig ist in welchen Gruppen er ist, welche Module und welche Seiten für diese Gruppe existieren.

        Nach deinem Ergebnis-Objekt zu schließen sind die Seiten die eigentliche Begierde und die Modul- und Gruppendaten das "Zubehör" - oder Pages sind die Master-Daten, Modules und Groups die Details. (Die Einschränkung nach einem bestimmten User mal unbeachtet gelassen.)

        Am Ende hätte ich gern ein Page-Objekt:

        object

        {
            // table.pages
            page array('pageID'     => $pageID,                                // Alias für table.pages.id
                       'pageData1'  => $pageData1,                             // Alias für Spalten
                       'pageData2'  => $pageData2,
                       /../)

        // table.modules
            modules array(moduleID1 => array('modulData1_1' => $modulData1_1,  // moduleID1 = Alias für table.modules.id
                                            'modulData1_2' => $modulData1_2),  // modulData1_1, 1_2,.. = Alias für Spalten
                          moduleID2 => array('modulData2_1' => $modulData2_1,
                                             /../)),

        // table.groups
            groups array(groupsID1  => array('groupData1_1' => $groupData1_1,   // groupsID1 = Alias für table.groups.id
                                             'groupData1_2' => $groupData1_2,   // groupData1_1, 1_2,.. = Alias für Spalten
                                             'groupData1_3' => $groupData1_3,   // aus table.groups
                                             /../),
                         groupsID2  => array(groupData2_1 => groupData2_1,
                                             /../))
        }

          
        Und das ist mit einer Abfrage zwar nicht unmöglich aber äußerst unhandlich zu bewerkstelligen. Das Ergebnis einer Abfrage ist eine flache Struktur: x Spalten und y Reihen. Du willst aber am Ende eine Baumstruktur haben, in der unterschiedliche Daten in den Zweigen stehen. Somit würde ich mehrere Abfragen vorschlagen. Die erste fragt die Seiten. Auch das wird schon ein Join-Monster, weil du, um nach den Nutzern einschränken zu können, über die Gruppen zu den Nutzern joinen muss - und das über die m:n-Verknüpfungstabellen. Als Ergebnis erhältst du die Page-Daten. Die nächste Abfrage ermittelt alle Gruppen-Daten zu den in den Page-Daten enthaltenen Page-IDs. (Page-IDs separieren und eien IN (...) Bedingung erstellen. Zu joinen brauchst du nur die m:n-Tabelle rel\_groups\_pages und das IN prüft auf deren pageID.) Das gleiche noch mit den Modulen.  
          
        Nicht betrachtet habe ich die Beziehung zwischen Modulen und Gruppen, weil mir deren Aufgabe noch nicht ganz klar ist. Warum gibt es eine Beziehung zwischen Seiten und Gruppen und noch eine zwischen Modulen und Seiten? Sind manche Seiten Modulen zugeordnet und ist der User über die Modul-Gruppen-Beziehungen und seiner Gruppenzugehörigkeit zu Handlungen an diesen Seiten berechtigt, auch wenn keine Zugehörigkeit zwischen Seite und Gruppe existiert? Und gibt es dann andererseits Seiten ohne Modulzugehörigkeit, die über einzelne Gruppen-Seiten-Verknüpfung dem User zugänglich sind? (Was ist, wenn Nutzer unabhängig von Gruppenzugehörigkeiten für Seiten berechtigt werden sollen? Dann hättest du noch einen weiteren Beziehungsweg.) Insgesamt scheint mir, dass du hier die Strukturen vereinfachen solltest, um diese zirkulären Beziehungen wegzubekommen. Vielleicht sollten Einzelseiten auch (Dummy-)Modulen zugeordnet werden müssen. Dann sind die Abfragen durch die m:n-Tabellen zwar immer noch nicht wirklich einfacher, aber der Weg ist eindeutig.  
          
          
        Lo!
        
        1. Hallo!

          Und das ist mit einer Abfrage zwar nicht unmöglich aber äußerst unhandlich zu bewerkstelligen. Das Ergebnis einer Abfrage ist eine flache Struktur: x Spalten und y Reihen. Du willst aber am Ende eine Baumstruktur haben, in der unterschiedliche Daten in den Zweigen stehen.

          Die Objekt-Struktur kann ich am Ende auch mit PHP erzeugen. Sinnvoll wäre in dem Fall natürlich ein Abfrage die lediglich auf wenig redundante Daten optimiert ist.

          Nicht betrachtet habe ich die Beziehung zwischen Modulen und Gruppen, weil mir deren Aufgabe noch nicht ganz klar ist. Warum gibt es eine Beziehung zwischen Seiten und Gruppen und noch eine zwischen Modulen und Seiten? Sind manche Seiten Modulen zugeordnet und ist der User über die Modul-Gruppen-Beziehungen und seiner Gruppenzugehörigkeit zu Handlungen an diesen Seiten berechtigt, auch wenn keine Zugehörigkeit zwischen Seite und Gruppe existiert?

          Ich merk schon, dass ich da wohl scheinbar etwas falsch gemacht habe wenn jeder daran moniert.
          Also der letzte Satz stimmt bis auf den letzten Teil. Eine Verknüpfung zwischen Seite und Gruppe sollte schon existieren.

          Und gibt es dann andererseits Seiten ohne Modulzugehörigkeit, die über einzelne Gruppen-Seiten-Verknüpfung dem User zugänglich sind?

          Gute Frage. Wird es Seiten ohne Module geben... Darüber hab ich noch nicht nachgedacht aber ich vermute einfach mal nein weil letztlich selbst Menüs Module sind. Oh Gott, nicht das meine ganze Datenplanung schon für den A**** ist und die DB-Struktur nur die Konsequenz davon. Ganz von vorne wollte ich eigentlich nicht anfangen :(

          (Was ist, wenn Nutzer unabhängig von Gruppenzugehörigkeiten für Seiten berechtigt werden sollen? Dann hättest du noch einen weiteren Beziehungsweg.)

          Da hast du recht. Auch darüber habe ich noch gar nicht nachgedacht :(

          Insgesamt scheint mir, dass du hier die Strukturen vereinfachen solltest, um diese zirkulären Beziehungen wegzubekommen. Vielleicht sollten Einzelseiten auch (Dummy-)Modulen zugeordnet werden müssen. Dann sind die Abfragen durch die m:n-Tabellen zwar immer noch nicht wirklich einfacher, aber der Weg ist eindeutig.

          Ok, also nochmal "from the scratch".
          Kein Problem, noch bin ich motiviert :)

          Also mein Problem/meine Aufgabe ist ja nun bekannt.
          Ich fange also mit neuen Tabellen an. Schritt für Schritt, damit ich nichts mehr verkehrt mache. Ich beginne also mit der Tabelle "groups". Soll ich denn eine Tabelle "user" über eine n:m-Tabelle verknüpfen? Auch das wurde ja bereits kritisiert wenn ich es richtig verstanden habe.

          Danke für deine Hilfe!

          Uwe

          1. Hi!

            Ich fange also mit neuen Tabellen an. Schritt für Schritt, damit ich nichts mehr verkehrt mache. Ich beginne also mit der Tabelle "groups". Soll ich denn eine Tabelle "user" über eine n:m-Tabelle verknüpfen? Auch das wurde ja bereits kritisiert wenn ich es richtig verstanden habe.

            Beschreibe bitte die Idee oder die Anwendungsfälle, die umgesetzt werden soll(en). Allein aus der Umsetzung kann man nicht sehen, ob die erstellten Struktur zur Lösung des Problems geeignet ist. Man kann sich dann nur Anwendungsfälle dazudenken, die sich damit gut und solche, die sich damit schlecht umsetzen lassen. Aber die stimmen dann mehr oder weniger nicht mit deinen überein.

            Ich denke jedoch, dass wir in "Svens Zweig" weitermachen sollten, sonst verzettelt es sich zu sehr.

            Lo!