SQL Design: User Settings

FrazeColder

Lt. Commander
Registriert
Okt. 2013
Beiträge
1.721
Moin zusammen,

ich bin gearde dabei eine Laravel Anwendung zu programmieren und mache mir gerade Gedanken darüber, wie ich am besten Userspezifische Einstellungen speichere. Es gibt online ja schon einige Vorschläge und auch GitHub Projekte, welche ich mir einfach zu Laravel dazu laden kann wie z.B. https://github.com/ricardoboss/laravel-user-settings.

Das Projekt von Github verfolgt den Ansatz einen JSON String in der User Tabelle zu speichern. Dann gibt es ja auch noch die Möglichkeit über ein key => value Pair und einer weiteren Tabelle zum jeweiligen User die Einstellungen zu speichern. Vorteil hierbei ist, man kann suchen und bekommt direkt die Setting, die man sucht. Nachteil, Speicherplatz.

Nun bin ich noch auf die Idee gekommen, das ganze über 3 Tabellen zu realisieren. Eine User Tabelle, eine Settings Tabelle und eine User_Settings Tabelle, die die User Settings verknüpft. Vorteil hiervon wäre die Skalierbarkeit. Ich weiß, dass bei meiner Anwendung noch einige Einstellungen in späteren Versionen hinzukommen werden. Das bei der JSON Lösung und der key => value Lösung für jeden User zu Updaten stelle ich mir schwerer vor, als das bei der Lösung mit 3 Tabellen zu machen. Für die Lösung mit den 3 Tabellen lass ich einfach ein SQL Statement drüberlaufen, welche die neue Einstellung und der Settings Tabelle anlegt und für alle User eine neue Verknüpfung mit der neuen Einstellung erstellt. Ich sehe bei der 3 Tabellen Lösung eigtl. keine Nachteile.

Wie seht ihr das? Welche Möglichkeit findet ihr besser und wieso?

MfG und Vielen Dank!
 
FrazeColder schrieb:
Nachteil, Speicherplatz.

So heftig groß sollten doch die Tabellen nicht werden. Wenn man sich noch irgendeine Codierung der Inhalte überlegt, dann sollte das auch nicht so groß werden.

Ich bin für die Variante mit den Tabellen statt JSON.
 
FrazeColder schrieb:
Vorteil hierbei ist, man kann suchen und bekommt direkt die Setting, die man sucht. Nachteil, Speicherplatz.
Ob du nach bestimmten Settings suchen willst, musst du selbst wissen. Das wäre das eigentlich ausschlaggebende Kriterium.
Der Speicherplatzbedarf ist irrelevant. Im Vergleich zu den restlichen Daten deiner Datenbank dürfte das ein feuchter Furz im Wind sein.

FrazeColder schrieb:
Für die Lösung mit den 3 Tabellen lass ich einfach ein SQL Statement drüberlaufen, welche die neue Einstellung und der Settings Tabelle anlegt und für alle User eine neue Verknüpfung mit der neuen Einstellung erstellt. Ich sehe bei der 3 Tabellen Lösung eigtl. keine Nachteile.
Ist in JSON doch genau so simpel - ob du nun ein SQL Query benutzt, oder das JSON bearbeitest.

FrazeColder schrieb:
Welche Möglichkeit findet ihr besser und wieso?
Ich speichere meine Settings als 1 JSON Dokument pro User. Die Settings werden fast ausschließlich vom Frontend genutzt. D.h. sie werden einmalig geladen, und das Frontend schreibt sie in den Cache. Aber ich muss auch nicht danach suchen.
Hat auch den Vorteil, dass du beliebige Datenstrukturen als "value" benutzen kannst. Um das in einer User_Settings Tabelle umzusetzen, müsstest du dafür dann auch wieder JSON Objekte als "value" verwenden.

Kommt halt alles wirklich auf deinen Anwendungsfall an.
 
benneq schrieb:
Ist in JSON doch genau so simpel - ob du nun ein SQL Query benutzt, oder das JSON bearbeitest.

Aber ist das nicht Zeitkomplexer jeden einzelnen User in der Tabelle durchzulaufen, das JSON zu laden, und dann neue Einstellungen hinzuzufügen, statt einfach ein SQL Befehl auszuführen, der für jeden User eine neue Verknüpfung anlegt?
 
Solange wir hier nicht über eine Anwendung mit Millionen von Usern und noch mehr Millionen von Settings reden, ist es in meinen Augen vollkommen unerheblich wie man das macht. Will heißen: Es gibt eher wenig Potential für Optimierungen, wenn es nur eine Handvoll Settings und eine Handvoll User gibt.

Zu deiner 3-Tabellen-Lösung möchte ich jedoch sagen, dass sie unnötig kompliziert ist. User-, Settings- und UserSettings-Tabelle kommen zum Einsatz, wenn du eine m:n Beziehung hast und das ist hier doch gar nicht der Fall. Jeder User hat genau ein Setting, also 1:1. Genau genommen ist also jedes einzelne Setting vom User abhängig und kann daher in einer normalisierten Datenbank auch in die Usertabelle aufgenommen werden, wie zB das Geburtsdatum. Ansonsten reicht in der Usertabelle auch ein FK auf die ID des Settings in der Settingstabelle, wenn man das separieren möchte.

https://www.datenbanken-verstehen.de/datenmodellierung/beziehungen-datenbanken/


*edit
Wobei es natürlich auf die Definition von "Setting" ankommt. Ich sehe es als Set von Einstellungen an, die in einer Tabelle (oder eben JSON-String) gespeichert werden, ein Datensatz wie "fullscreen=1, fgcolor=white, fgcolor=black"
 
  • Gefällt mir
Reaktionen: RalphS
FrazeColder schrieb:
Aber ist das nicht Zeitkomplexer jeden einzelnen User in der Tabelle durchzulaufen, das JSON zu laden, und dann neue Einstellungen hinzuzufügen, statt einfach ein SQL Befehl auszuführen, der für jeden User eine neue Verknüpfung anlegt?

Würde mich auch interessieren.
Wenn man die Tabellen indiziert, dann sollte meinem Verständniss nach die Datenbanklösung performanter sein.
 
@FrazeColder Ja natürlich wird das mehr Zeit in Anspruch nehmen. Du solltest dich halt fragen: Wie oft kommt das vor, dass du sämtliche Settings updatest? Und: Lohnt sich da eine Optimierung wegen ein paar Sekunden überhaupt?
 
  • Gefällt mir
Reaktionen: Raijin
Der Vorteil einer Lösung mit JSON ist natürlich, dass man problemlos weitere Einstellungen hinzufügen kann, ohne das Datenbankdesign anzufassen. Es ist ja nur ein String, der dann innerhalb der Anwendung interpretiert wird. Neue Einstellung, neues Property vs Neue Einstellung, Redesign der Datenbank.
 
@Raijin Du hast seine Idee noch nicht ganz verstanden:
Code:
User_id | Setting_id | Setting_value
------------------------------------
4       | 42         | true
4       | 43         | "foo"
5       | 42         | false
5       | 43         | "bar"
(Oder statt Setting_id eben den konkreten eindeutigen Namen des Settings (z.B. "locale" oder "fullscreen") - je nach Präferenz. Mit Unique Index auf (User_id, Setting_id) )
Man müsste also nur auf dieser Tabelle ein Update ausführen, das für alle User_ids eine neue Zeile mit dem neuen Setting und Default Wert einfügt - kein Redesign notwendig.

Aber ich finde es immer noch nachteilig, dass man ausschließlich Strings als Datentyp verwenden kann - aus Sicht der Datenbank. Bzw. wenn man dort JSON Objekte als Value speichert, dann braucht's wieder einen JSON Mapper, um die Daten lesen zu können.
 
Also im Sinne der Datenbank Normalformen würde ich kein JSON als String in eine Spalte schreiben.
Viele relationale Datenbanksysteme unterstützen allerdings inzwischen einen JSON-Datentyp, welcher es erlaubt dieses JSON-Objekt dann in einem Query zu durchsuchen oder einzelne felder im Json zu aktualisieren. Außerdem können mithilfe von virtuellen Spalten auch Indizes auf Daten in JSON Spalten erstellt werden. Wenn du es als String speichern möchtest, solltest du mal prüfen ob deine Datenbank dies unterstützt.
 
benneq schrieb:
Du hast seine Idee noch nicht ganz verstanden
Das mag sein, deswegen schrieb ich ja im *edit , dass es auf die Definition von "Settings" ankommt. :schluck:
 
Raijin schrieb:
Der Vorteil einer Lösung mit JSON ist natürlich, dass man problemlos weitere Einstellungen hinzufügen kann, ohne das Datenbankdesign anzufassen. Es ist ja nur ein String, der dann innerhalb der Anwendung interpretiert wird. Neue Einstellung, neues Property vs Neue Einstellung, Redesign der Datenbank.

Kann ich doch bei dem 3 Spalten Design auch und sogar noch viel einfacher?
Ich lege die neue Einstellung in der Tabelle Settings an und füge einfach für jeden User eine neue Verknüpfung in der Users_Settings Tabelle ein.

In der JSON Variante müsste ich hingehen und durch jede Zeile itterieren...
 
  • Gefällt mir
Reaktionen: G-Red
Wie gesagt, "Settings" ist ein seeeeeeeeeeeeeeeeehr dehnbarer Begriff. Ich definiere das anders als du und wenn du kein Beispiel postest, lässt du nun mal viel Spielraum für Interpretation.
 
So ganz grundsätzlich: Geh erst mal den einfachsten Weg, der deine Anforderungen erfüllt. Und wenn du später merkst, dass du mehr / andere Features brauchst, die mit der aktuellen Lösung nicht machbar sind, dann überlegst du dir was neues.

Es bringt dir ja schließlich herzlich wenig, wenn du jetzt eine super-duper-kann-sogar-Kaffee-kochen Lösung bastelst, die viele viele Stunden Arbeit kostet, aber in Wirklichkeit benötigst du nie mehr als die Basics, die man auch in 30 Minuten hätte umsetzen können.
 
M x N macht nur Sinn, wenn jeder User eine unterschiedliche Settings-Konfiguration haben kann.

User A kriegt zB Settings a, b und c mit den Werten 1, true und "nein".
Und User B kriegt stattdessen Settings q, r und k mit den Werten hidden, null und 0x12.

"Normalerweise" ist das nicht der Fall; normalerweise hat man in einer Einwendung eine definierte Menge an Settings, die für jeden User gelten.

Man kann das in eine 1:1 Relation rausziehen, wenn es viele settings gibt. Ansonsten bleibt es Teil der Usertabelle.

UserId Name Vorname SettingA SettingB SettingC SettingD etc.

Wenn sich was erweitert ist auch nicht viel Redesign. Spalte hinzufügen und referenzieren. Wem das zu viel ist, dem ist auch 1:n mit einer extra Settings tabelle zuviel und zwei für M x N mit Mapping dazwischen sowieso.


Nicht umständlicher machen als nötig. Wenn JSON hinten rauskommen muß, kann man durchaus eine Klasse in der Anwendung implementieren, die man dann mit userid=n instantiiert und dann hinten ein JSON-Objekt mit den Settings für diese UserID bekommt. Das macht man einmal und nie wieder und guckt sich das nur an, wenn sich an der Settingskonfig was ändern sollte.
 
  • Gefällt mir
Reaktionen: Raijin und BeBur
+1 RalphS. Damit ist auch die Realisierung von Default-Werten einfacher.
Bei postgres könnte man z.B. auch mit enums arbeiten bei Bedarf
 
Mein Projekt hätte tastächlich für jeden User unterschiedliche Einstellungen. Ich rede hier ja nicht von Rollen oder Berechtigungen, sondern wirklich Einstellungen wie Newsletter an/aus, Notifications an/aus, Light Theme/Dark Theme an/aus, Anzeige Format 1/2/3, etc....

Denke da wäre eine N:M Verbindung echt am besten.
 
Bei Postgres kannst eine Defaultkonfig in einer Settings-Tabelle haben und davon erben.

ORDBMS ist was Feines. Leider gibt es das kaum und Postgres versteht auch kaum wer, was man da in der Hand hat.

===
@FrazeColder
Eben nicht, Du wirfst da grad zuviel zusammen.

JEDER Nutzer kann Newsletter konfigurieren, kann Notifications verlangen und so weiter.

Tabellen sind ZWEIdimensional.
UserID Notifcations Newsletter ....
1 ja nein
2 nein nein
3 nein ja
4 ja ja
....
 
  • Gefällt mir
Reaktionen: BeBur und Raijin
Genau darauf wollte ich eigentlich hinaus, aber @RalphS hat es mal wieder auf den Punkt gebracht ;)
 
Ich bring was auf den •Punkt•? Wow. Daß ich das noch mal hören darf. ☺


Ob die Settings jetzt in eine eigene Tabelle sollten oder nicht...leitet sich hauptsächlich aus der Antwort auf die Frage ab, ob die Settings nun eine eigene Dimension repräsentieren oder nicht. Sprich, ob die verschiedenen Einstellmöglichkeiten unbeschränkt wachsen können oder nicht.

-- Es gibt eine fixe Setting-Konfiguration, die sich auch nur sehr wenig ändern wird?
=> Keine eigene Dimension und keine Settingstabelle erforderlich (oder besonders sinnvoll).
Einfach zu handhaben, aber auch recht statisch. Für neue Settings muß die Anwendung angepaßt werden und praktisch wird ein neues Release erforderlich.

-- Es kommen ständig neue Settings dazu, das soll on the fly passieren und/oder es sollen auch Anwender welche anlegen können?
=> Eigene Settingstabelle plus Zuordnung (User, setting, value).
Aufwendig, dafür flexibler.
Layout dafür wäre sowas wie
users = id, Vorname,...
settings= settingid, settingname, typeid, defaultvalue, ...
values= uid,setid,setvalue, ...
types= tid, Name, Definition,...

Man muß die Anwendung nicht mehr anfassen, wenn man sie geschickt programmiert hat, aber es gibt signifikanten Extraaufwand — zb muß sich was einfallen lassen für Standardwerte, vielleicht indem man diese an den Settings definiert. Plus man hat das Problem, Datentypen des DBMS nicht nutzen zu können, weil values.value nur exakt einen haben kann. Also muß jedes Setting seinen Inhalt selbst deklarieren und die Anwendung muß Datentypen selber auflösen können... das geht nur bis zu einem bestimmten Punkt gut, sodaß die typtabelle so oder so endlich bleiben muß und man sich mit weiteren abstrahierten Trennungen zwischen Anwendung und Daten herumärgern muß.

Und nicht zu vergessen muß berücksichtigt werden, daß kein einziger Anwender notwendigerweise ein Setting konfiguriert haben muß, sprich man braucht outer joins und coalesce bzw. passende Anwendungslogik. Einfach select setid,value from values genügt nicht, wo ansonsten select settingname from users where uid =? genügt hätte.

Also ja. KANN man machen. Aber man sollte es IMO nur, wenn man sich einen ausreichenden Bonus davon verspricht und wenn man sich etwaiger Konsequenzen bewußt ist und diese berücksichtigen kann (neues Setting eingetragen > sehen gleich alle und können es verwenden > will ich aber nicht > Attribute Visible in der Setting-Tabelle mit default false).

Einfach mal so eben ist es den Ansatz IMO nicht wert, aber wenn der Anspruch da ist, dann muß man wohl.
 
Zurück
Oben