Wie kann ich die Kontrolle über eine SQL-Tabelle zugreifen kann?
-
03-07-2019 - |
Frage
Ich versuche, ein Zutrittskontrollsystem zu schaffen.
Hier ist ein abgespeckte Beispiel dafür, was die Tabelle Ich versuche, wie Zugang zu Aussehen zu steuern:
things table:
id group_id name
1 1 thing 1
2 1 thing 2
3 1 thing 3
4 1 thing 4
5 2 thing 5
Und die Zugriffssteuerungstabelle sieht wie folgt aus:
access table:
user_id type object_id access
1 group 1 50
1 thing 1 10
1 thing 2 100
Der Zugang kann gewährt werden, indem entweder die ID des ‚Dings‘ spezifiziert direkt oder für eine ganze Gruppe von Dingen, gewährt durch eine Gruppe-ID angeben. In dem obigen Beispiel Benutzer 1 wurde eine Zugriffsstufe von 50 bis Gruppe 1 gewährt, es sei denn es andere Regeln gelten sollten präziser Zugriff auf eine einzelne Sache zu gewähren.
Ich brauche eine Abfrage, die eine Liste der Dinge gibt (ids nur in Ordnung ist) zusammen mit der Zugriffsebene für einen bestimmten Benutzer. So am Beispiel oben ich so etwas wie dies für Benutzer-ID 1 wollen würde:
desired result:
thing_id access
1 10
2 100
3 50 (things 3 and 4 have no specific access rule,
4 50 so this '50' is from the group rule)
5 (thing 5 has no rules at all, so although I
still want it in the output, there's no access
level for it)
Die nächstgelegene ich mit oben kommen kann, ist dies:
SELECT *
FROM things
LEFT JOIN access ON
user_id = 1
AND (
(access.type = 'group' AND access.object_id = things.group_id)
OR (access.type = 'thing' AND access.object_id = things.id)
)
Aber das gibt mehrere Zeilen, wenn ich für jede Zeile nur eine will in der Tabelle ‚Dinge‘. Ich bin mir nicht sicher, wie für jedes ‚Ding‘, auf eine einzige Zeile zu erhalten oder, wie zu priorisieren ‚Ding‘ Regeln über ‚Gruppe‘ Regeln.
Wenn es hilft, die Datenbank ich benutze ist PostgreSQL.
Bitte fühlen Sie sich frei um einen Kommentar zu hinterlassen, wenn es irgendwelche Informationen, das ich verpasst habe aus.
Vielen Dank im Voraus!
Lösung
Ich weiß nicht, den Postgres SQL-Dialekt, aber vielleicht so etwas wie:
select thing.*, coalesce ( ( select access
from access
where userid = 1
and type = 'thing'
and object_id = thing.id
),
( select access
from access
where userid = 1
and type = 'group'
and object_id = thing.group_id
)
)
from things
Übrigens, ich mag nicht das Design. Ich würde die Zugriffstabelle lieber in zwei werden:
thing_access (user_id, thing_id, access)
group_access (user_id, group_id, access)
Meine Abfrage wird dann:
select thing.*, coalesce ( ( select access
from thing_access
where userid = 1
and thing_id = thing.id
),
( select access
from group_access
where userid = 1
and group_id = thing.group_id
)
)
from things
Ich ziehe dies, weil Fremdschlüssel kann nun in den Access-Tabellen verwendet werden.
Andere Tipps
Ich habe gerade gelesen ein Papier gestern Abend zu diesem Thema. Es hat einige Ideen, wie dies zu tun. Wenn Sie nicht auf den Link auf den Titel verwenden können, versuchen Sie es mit Google Scholar auf Disclosure Begrenzung in hippokratischen Datenbanken.
Zwar gibt es mehrere gute Antworten sind, die effizienteste wahrscheinlich so etwas wie dieses wäre:
SELECT things.id, things.group_id, things.name, max(access)
FROM things
LEFT JOIN access ON
user_id = 1
AND (
(access.type = 'group' AND access.object_id = things.group_id)
OR (access.type = 'thing' AND access.object_id = things.id)
)
group by things.id, things.group_id, things.name
Welche verwendet einfach Verdichtungs hinzugefügt, um Sie abfragen, um zu bekommen, was Sie suchen.
Tony:
Keine schlechte Lösung, ich mag es, scheint zu funktionieren. Hier ist Ihre Abfrage nach kleinerem Tweaking:
SELECT
things.*,
coalesce (
( SELECT access
FROM access
WHERE user_id = 1
AND type = 'thing'
AND object_id = things.id
),
( SELECT access
FROM access
WHERE user_id = 1
AND type = 'group'
AND object_id = things.group_id
)
) AS access
FROM things;
Und die Ergebnisse sehen richtig:
id | group_id | name | access
----+----------+---------+--------
1 | 1 | thing 1 | 10
2 | 1 | thing 2 | 100
3 | 1 | thing 3 | 50
4 | 1 | thing 4 | 50
5 | 2 | thing 5 |
ich vollständig nehmen Sie den Punkt, um es kein ideales Schema zu sein. Allerdings bin ich mit ihm zu einem gewissen Grad fest.
Josef:
Ihre Lösung ist sehr ähnlich wie die Sachen, die ich mit spiele, und meine Instinkte (wie sie sind) sagt mir, dass es möglich sein sollte, es so zu tun. Leider produziert es nicht ganz korrekt Ergebnisse:
id | group_id | name | max
----+----------+---------+-----
1 | 1 | thing 1 | 50
2 | 1 | thing 2 | 100
3 | 1 | thing 3 | 50
4 | 1 | thing 4 | 50
5 | 2 | thing 5 |
Die Zugriffsebene für ‚Ding 1‘ hat die höhere ‚Gruppe‘ Zugriffswert genommen, anstatt die speziellere ‚Ding‘ Zugriffswert von 10, das ist das, was ich bin nach. Ich glaube nicht, gibt es eine Möglichkeit, dass innerhalb eines GROUP BY
zu beheben, aber wenn jemand irgendwelche Vorschläge ich mehr als glücklich bin zu in diesem Punkt falsch nachgewiesen werden.