Wie würden Sie eine Benutzerdatenbank mit benutzerdefinierten Feldern entwerfen?

18

Diese Frage dreht sich darum, wie ich eine Datenbank entwerfen soll, es kann sich um relationale / nosql-Datenbanken handeln, je nachdem, welche die bessere Lösung sein wird


Unter der Voraussetzung, dass Sie ein System erstellen müssen, das eine Datenbank zur Verfolgung von "Unternehmen" und "Benutzer" umfasst. Ein einzelner Benutzer gehört immer nur einer Firma an

  • Ein Benutzer kann nur einer Firma angehören
  • Ein Unternehmen kann viele Benutzer haben

Das Design für den "Company" -Tisch ist recht unkompliziert. Firma wird die folgenden Attribute / Spalten haben: (Lassen Sie es uns einfach halten)

ID, COMPANY_NAME, CREATED_ON

Erstes Szenario

Einfach und unkompliziert, Benutzer haben alle das gleiche Attribut. Dies kann also einfach in relationaler Art und Weise durchgeführt werden. Benutzertabelle:

ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, CREATED_ON

Zweites Szenario

Was passiert, wenn verschiedene Unternehmen unterschiedliche Profilattribute für ihren Benutzer speichern möchten? Jedes Unternehmen verfügt über definierte Attribute, die für alle Benutzer dieses Unternehmens gelten.

Beispielsweise:

  • Firma A möchte speichern: LIKE_MOVIE (boolean), LIKE_MUSIC (boolean)
  • Firma B möchte speichern: FAV_CUISINE (String)
  • Firma C möchte speichern: OWN_DOG (boolean), DOG_COUNT (int)

Ansatz 1

Die Brute-Force-Methode besteht darin, ein einziges Schema für den Benutzer zu haben und Nullen zuzulassen, wenn sie nicht zur Firma gehören:

ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, LIKE_MOVIE, LIKE_MUSIC, FAV_CUISINE, OWN_DOG, DOG_COUNT, CREATED_ON

Das ist ein bisschen böse, weil Sie am Ende viele NULL-Werte und Benutzerzeilen haben, deren Spalten für sie irrelevant sind (dh, alle Benutzer, die zu Unternehmen A gehören, haben NULL-Werte für FAV_CUISINE, OWN_DOG, DOG_COUNT).

Ansatz 2

Ein zweiter Ansatz ist, ein "Freiformfeld" zu haben:

ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, CUSTOM_1, CUSTOM_2, CUSTOM_3, CREATED_ON

Was für sich genommen unangenehm wäre, da Sie keine Ahnung haben, was benutzerdefinierte Felder sind, spiegelt der Datentyp nicht die gespeicherten Werte wider (z. B. speichern wir den Wert int als VARCHAR).

Ansatz 3

Ich habe mir das PostgreSQL-JSON-Feld angesehen. In diesem Fall haben Sie:

ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, CUSTOM_PROFILE_JSON, CREATED_ON

Wie können Sie in diesem Fall verschiedene Schemas auf einen Benutzer anwenden? Ein Benutzer mit Firma A hat ein Schema, das so aussieht

 {"LIKE_MOVIE":"boolean", "LIKE_MUSIC": "boolean"}

Während ein Benutzer mit Firma C ein anderes Schema hat:

 {"OWN_DOG ":"boolean", "DOG_COUNT": "int"}

Wie soll ich dieses Problem lösen? Wie kann ich die Datenbank richtig entwerfen, um dieses flexible Schema für ein einzelnes "Objekt" (Benutzer) basierend auf der Beziehung, die sie haben (Firma), zu ermöglichen?

relationale Lösung? nosql lösung?


Bearbeiten: Ich habe auch über eine "CUSTOM_PROFILE" -Tabelle nachgedacht, in der Benutzerattribute im Wesentlichen in Zeilen und nicht in Spalten gespeichert werden.

Bei diesem Ansatz gibt es zwei Probleme:

1) Die Datenmenge pro Benutzer wächst eher als Zeilen als als als Spalten. Dies bedeutet, dass für ein vollständiges Bild des Benutzers eine Vielzahl von Verknüpfungen durchgeführt werden muss und mehrere Verknüpfungen mit der Tabelle "Benutzerdefiniertes Profil" für die verschiedenen benutzerdefinierten Attribute erforderlich sind

2) Der Datenwert wird immer als VARCHAR gespeichert, um generisch zu sein, auch wenn wir wissen, dass die Daten ganzzahlig oder boolesch usw. Sein sollen

Noobcser
quelle
3
Wenn verschiedene Unternehmen unterschiedliche, mehrwertige Datensätze für jeden Kunden haben, benötigen Sie unbedingt eine Verknüpfungstabelle COMPANY_CUSTOMER. Alles andere wird dir sehr bald große Schmerzen bereiten.
Kilian Foth
Wie würde eine Verknüpfungstabelle mit den benutzerdefinierten Daten helfen? Die Spalten müssen noch anders sein
noobcser
1
Sie müssen die Tatsache "Kilians Passwort für IKEA ist 'Kätzchen'" mit einem Tupel wie "FIRMA: IKEA, KUNDE: Kilian, ATTRIBUT: Passwort, WERT: Kätzchen" darstellen. Einfacher geht es nicht.
Kilian Foth
3
Ein Schema ist per Definition eine feste Sache; Sie können keine einrichten, wenn Sie nicht wissen, welche Felder Sie benötigen. Werfen Sie einen Blick auf Entity-Attribute-Value, um herauszufinden, ob Probleme wie diese in einer relationalen Datenbank gelöst werden.
Mason Wheeler

Antworten:

13

Bitte betrachten Sie dies als Alternative. In den beiden vorherigen Beispielen müssen Sie Änderungen am Schema vornehmen, da der Anwendungsbereich wächst. Außerdem ist es schwierig, die Lösung "custom_column" zu erweitern und zu verwalten. Schließlich haben Sie Custom_510 und stellen sich vor, wie schrecklich diese Tabelle sein wird, mit der Sie arbeiten können.

Lassen Sie uns zuerst Ihr Firmenschema verwenden.

[Companies] ComnpanyId, COMPANY_NAME, CREATED_ON

Als Nächstes verwenden wir Ihr Benutzerschema auch für die erforderlichen Attribute der obersten Ebene, die von allen Unternehmen verwendet bzw. gemeinsam genutzt werden.

[Users] UserId, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, CREATED_ON

Als Nächstes erstellen wir eine Tabelle, in der wir unsere dynamischen Attribute definieren, die für die benutzerdefinierten Benutzerattribute der einzelnen Unternehmen spezifisch sind. Ein Beispielwert für die Attributspalte wäre "LikeMusic":

[UserAttributeDefinition] UserAttributeDefinitionId, CompanyId, Attribute

Als Nächstes definieren wir eine UserAttributes-Tabelle, die Benutzerattributwerte enthält

[UserAttributes] UserAttributeDefinitionId, UserId, Value

Dies kann auf viele Arten geändert werden, um die Leistung zu verbessern. Sie können mehrere Tabellen für UserAttributes verwenden, wobei jede für den in Value gespeicherten Datentyp spezifisch ist, oder Sie lassen sie einfach als VarChar und arbeiten mit ihr als Schlüsselwertspeicher.

Möglicherweise möchten Sie auch CompanyId aus der UserAttributeDefiniton-Tabelle in eine Querverweistabelle verschieben, um eine spätere Überprüfung durchzuführen.

P. Roe
quelle
Danke - ich denke über einen solchen Ansatz nach - siehe Bearbeiten. 2 Probleme: 1) Die Daten werden als Zeilen vergrößert, was bedeutet, dass Sie eine Menge Verknüpfungen ausführen müssen, um ein vollständiges Bild eines Benutzers zu erhalten. 2) "value" wird immer als VARCHAR gespeichert, um generisch zu sein, auch wenn der Wert tatsächlich int oder boolean usw. ist
noobcser
1
Wenn Sie int / bigint für die Tabellenidentitäten verwenden und diese verknüpfen, treten keine Leistungsprobleme auf, bis Sie sich in einer extremen Anzahl von Zeilen befinden. Wenn Sie nun mit der Suche basierend auf den Attributwerten beginnen, kann dies ein Problem darstellen, wenn Sie eine große Anzahl von Datensätzen abrufen. In diesem Fall würde ich mit einem Datenbankadministrator zusammenarbeiten, um festzustellen, ob Indizes erstellt oder eine indizierte Ansicht verwendet werden kann, mit der diese Suchvorgänge beschleunigt werden können. Ich habe ein ähnliches Schema verwendet und es werden 100 Millionen Datensätze pro Jahr ohne Leistungsprobleme erstellt, sodass das Basisdesign ziemlich gut funktioniert IMO
P. Roe
Wenn Berichterstellung, Filterung und Abfrage erforderlich sind und unterschiedliche Attribute zu unterschiedlichen Datensätzen gehören können. Wäre dieser Ansatz besser als NoSQL? Ich versuche den Leistungsunterschied zu verstehen. Ähnliche Situation nur Benutzer können Berichte definieren, die benutzerdefinierte Felder enthalten.
Kos
Wie implementieren wir in dem obigen Ansatz das Suchding als diff? Unternehmen möchten nach Feldern suchen, auch nach Benutzerfeldern. Was ist der richtige Ansatz, um darüber hinaus eine skalierbare Suche zu ermöglichen
techagrammer
Sie können es normalerweise mit vielen Verknüpfungen durchsuchen. Sie können ein ETL-Skript verwenden, um die zu durchsuchenden Daten zu extrahieren und in einer stärker denormalisierten Struktur abzulegen. Zuletzt können Sie versuchen, indizierte Ansichten als Suchmethode zu verwenden. Persönlich empfehle ich die ETL-Methode, um denormalisierte Strukturen zu generieren, die einfach zu suchen sind.
P. Roe
7

Verwenden Sie eine NoSQL-Datenbank. Es würde Firmen- und Benutzerdokumente geben. Die Benutzer würden einen Teil ihres Schemas basierend auf einer Benutzervorlage dynamisch erstellen lassen (Text zur Angabe von Feldern / Typen für diese Firma).

\Company\<uniqueidentifier>
    - Name: <Name>
    - CreatedOn: <datetime>
    - UserTemplate: <Text>

\User\<uniqueidentifier>
    - COMPANY_ID: <ID>
    - FIRST_NAME: <Text>
    - LAST_NAME: <Text>
    - EMAIL: <Text>
    - CREATED_ON: <datetime>
    - * Dynamically created fields per company

So könnte es in so etwas wie Firebase.com aussehen. Sie müssten lernen, wie man es in einem beliebigen Programm macht.

JeffO
quelle
Dies ist, woran ich denke, oder vielleicht JSON-Spalten. Wie ist die Leistung beim Abfragen und Filtern von Berichten im Vergleich zu der von PRoe vorgeschlagenen Lösung?
Kos
1
Jedes Mal, wenn Sie Daten in json oder xml komprimieren und sie dann in eine Spalte werfen, ist die Suche äußerst langsam. Wenn Sie die in meiner Antwort oben angegebenen Daten durchsuchen müssen, empfehle ich die Verwendung von indizierten Ansichten, um die Daten abzurufen. Wenn diese Lösung nicht ideal ist, empfehle ich die Verwendung von ETL, um die Daten in eine Struktur zu kopieren, die leicht durchsucht und dokumentiert werden kann.
P. Roe
Wie implementieren wir in dem obigen Ansatz das Suchding als diff? Unternehmen möchten nach Feldern suchen, auch nach Benutzerfeldern. Was ist der richtige Ansatz, um darüber hinaus eine skalierbare Suche zu ermöglichen
techagrammer
In nosql-Datenbanken sind möglicherweise redundante Daten vorhanden, die jedoch so strukturiert sind, dass sie durchsucht werden können. Die oben gezeigte ist nach eindeutiger Kennung. Ein anderer könnte \ Company \ Name sein. Es ist ähnlich wie bei mehreren Indizes.
JeffO
3

Wenn Sie häufig mit benutzerdefinierten Feldanforderungen konfrontiert werden, modelliere ich diese ziemlich ähnlich wie die Datenbank. Erstellen Sie eine Tabelle mit den Metadaten zu jedem benutzerdefinierten Feld, CompanyCustomField (zu wem es gehört, dem Datentyp usw.) und einer weiteren Tabelle CompanyCustomFieldValues, die die CustomerId, FieldId und den Wert enthält. Wenn Sie so etwas wie Microsoft SQL Server verwenden, müsste die Wertespalte ein sql_variant-Datentyp sein.

Dies ist natürlich nicht einfach, da Sie eine Schnittstelle benötigen, über die Administratoren benutzerdefinierte Felder für jeden Kunden definieren können, und eine weitere Schnittstelle, die diese Metadaten verwendet, um eine Benutzeroberfläche zum Erfassen der Feldwerte zu erstellen. Und wenn Sie andere Anforderungen haben, z. B. das Gruppieren von Feldern oder das Erstellen einer Auswahlliste, müssen Sie dies mit mehr Metadaten / anderen Tabellen (z. B. CompanyCustomFieldPickListOptions) abgleichen.

Dies ist nicht trivial, hat aber den Vorteil, dass keine Datenbankänderungen / Codeänderungen für jedes neue benutzerdefinierte Feld erforderlich sind. Alle anderen Funktionen von benutzerdefinierten Feldern müssen ebenfalls codiert werden (z. B. wenn Sie einen Zeichenfolgenwert durch einen regulären Ausdruck überprüfen möchten oder nur Datumsangaben zwischen bestimmten Bereichen zulassen möchten oder wenn Sie ein benutzerdefiniertes Feld basierend auf einem anderen benutzerdefinierten Feldwert aktivieren müssen ).

Andy
quelle
Danke - ich denke über einen solchen Ansatz nach - siehe Bearbeiten. 2 Probleme: 1) Die Daten werden als Zeilen vergrößert, was bedeutet, dass Sie eine Menge Verknüpfungen ausführen müssen, um ein vollständiges Bild eines Benutzers zu erhalten. 2) "value" wird immer als VARCHAR gespeichert, um generisch zu sein, auch wenn der Wert tatsächlich int oder boolean usw. ist
noobcser
1
@noobcser Das Datenwachstum als Zeilen spielt keine Rolle, nachdem alle Datenbanken um Zeilen und Verknüpfungen herum entworfen wurden. In jedem Fall würden Sie dafür eher Common Table Expressions verwenden, die in solchen Dingen ziemlich gut sind. Ich bin mir nicht sicher, ob Sie den Teil verpasst haben, in dem Sie "sql_variant" als Datentyp für die Wertspalte verwenden können, in der der Wert als der Typ gespeichert wird, in den Sie sich einfügen. Während ich MS SQL Server-Funktionsnamen benenne, würde ich erwarten, dass andere ausgereifte DBMS ähnliche Funktionen haben.
Andy
1
@noobcser FYI Ich bin in meiner Karriere häufig auf diese Anforderungen gestoßen und habe Erfahrung mit jeder der vorgeschlagenen Lösungen. Daher schlage ich die vor, die meiner Erfahrung nach am besten funktioniert hat. Die Verwendung von XML-Datentypen für diese Art von Dingen ist teilweise der Grund, warum ich es hasse, dass MS XML als systemeigenen Datentyp hinzufügt.
Andy
1

Eine Alternative zu den anderen Antworten besteht darin, eine Tabelle namens profile_attrib oder eine ähnliche Tabelle zu haben, die das Schema vollständig von Ihrer Anwendung verwaltet.

Wenn benutzerdefinierte Attribute hinzugefügt werden, ALTER TABLE profile_attrib ADD COLUMN like_movie TINYINT(1)können Sie das Löschen verhindern. Dies würde Ihren Beitritt minimieren und dennoch Flexibilität bieten.

Ich vermute, der Nachteil ist, dass die Anwendung jetzt die Berechtigung zum Ändern von Tabellen für die Datenbank benötigt, und Sie müssen gescheit sein, die Spaltennamen zu bereinigen.

Chris Seufert
quelle
Der reguläre Ausdruck [^\w-]+sollte es ziemlich gut machen und nichts 0-9A-Za-z_-zulassen, was nicht ist - aber ja, Desinfektion ist hier ein Muss, um vor Böswilligkeit oder Dummheit zu schützen.
Regelmäßige Joe
0

Ihre Frage hat viele mögliche Lösungen. Eine Lösung besteht darin, die zusätzlichen Attribute als XML zu speichern. Das XML kann als Text gespeichert werden oder wenn Sie eine Datenbank verwenden, die XML-Typen als XML unterstützt (SQL Server). Das Speichern als Text schränkt Ihre Abfragemöglichkeit ein (wie das Suchen nach einem benutzerdefinierten Attribut), aber wenn Sie nur speichern und abrufen müssen, ist dies eine gute Lösung. Wenn eine Abfrage erforderlich ist, ist das Speichern der XML-Datei als XML-Typ eine bessere Option (obwohl dies herstellerspezifischer ist).

Dies gibt einem die Möglichkeit, eine beliebige Anzahl von Attributen für einen Kunden zu speichern, indem lediglich eine zusätzliche Spalte in der Kundentabelle hinzugefügt wird. Man könnte die Attribute als Hashset oder Wörterbuch speichern, man verliert die Typensicherheit, da alles eine Zeichenfolge ist, aber wenn man eine Standardformatzeichenfolge für Datumsangaben, Zahlen und Boolesche Werte erzwingt, funktioniert dies in Ordnung.

Für mehr Informationen:

https://msdn.microsoft.com/en-us/library/hh403385.aspx

@ WalterMittys Antwort ist ebenfalls gültig. Wenn man jedoch viele Kunden mit unterschiedlichen Attributen hat, könnte man nach dem Vererbungsmodell viele Tabellen erhalten. Dies hängt davon ab, wie viele benutzerdefinierte Attribute von Kunden gemeinsam genutzt werden.

Jon Raynor
quelle
Dies kann auch funktionieren, aber ich bin der Meinung, dass Sie eingeschränkt werden, wenn Sie tatsächlich etwas gegen die im XML / JSON-Feld gespeicherten Daten tun müssen.
Andy
@Andy - Es stimmt, es gibt eine andere Ebene. Abfrage-DB und Analysieren von XML im Gegensatz zu nur Abfrage-DB. Ich weiß nicht, ob ich es einschränkend nennen würde, nur umständlicher. Es wäre jedoch eine Überlegung wert, wenn die benutzerdefinierten Attribute ausgiebig verwendet würden.
Jon Raynor
In T-SQL ist es möglich, den Inhalt in der XML / JSON-Spalte für einen Namespace zu definieren und Elemente in den benutzerdefinierten Daten abzufragen. Es ist nicht schwer
Stephen York
-1

Sie sollten Ihre Datenbank so normalisieren, dass Sie für jede Art von Unternehmensprofil 3 verschiedene Tabellen haben. In Ihrem Beispiel hätten Sie Tabellen mit Spalten:

USER_ID, LIKE_MOVIE, LIKE_MUSIC

USER_ID, FAVORITE_CUISINE

USER_ID, OWN_DOG, DOG_COUNT

Bei diesem Ansatz wird davon ausgegangen, dass Sie die Form der Informationen kennen, die ein Unternehmen speichern möchte, und dass sie sich nicht häufig ändern. Wenn die Form der Daten zur Entwurfszeit unbekannt ist, ist es wahrscheinlich besser, dieses JSON-Feld oder eine nosql-Datenbank zu verwenden.

Mortalapeman
quelle
-1

Aus dem einen oder anderen Grund sind Datenbanken das Feld, in dem der Effekt der inneren Plattform am häufigsten auftritt. Dies ist nur ein weiterer Fall des Auftauchens des Anti-Patterns.

In diesem Fall versuchen Sie, die natürliche und korrekte Lösung zu finden. Die Benutzer von Unternehmen A sind keine Benutzer von Unternehmen B, und sie sollten ihre eigenen Tabellen für ihre eigenen Felder haben.

Ihr Datenbankanbieter stellt Ihnen keine Gebühren für die Tabelle in Rechnung, und Sie benötigen nicht den doppelten Speicherplatz für die doppelten Tabellen (zwei Tabellen sind in der Tat effizienter, da Sie die Attribute von A nicht für die Benutzer von B speichern. Auch wenn Sie nur NULL-Werte speichern braucht Platz).

Wenn genügend gemeinsame Felder vorhanden sind, können Sie diese natürlich in eine gemeinsam genutzte Benutzertabelle einbeziehen und in jeder der firmenspezifischen Benutzertabellen einen Fremdschlüssel haben. Dies ist eine so einfache Struktur, dass kein Datenbankabfrageoptimierer damit zu kämpfen hat. Jeder notwendige JOIN ist trivial.

MSalters
quelle
3
Und wenn Sie Tausende von Kunden haben, kann eine Tabelle pro Kunde schnell nicht mehr erreichbar sein, ganz zu schweigen davon, dass Sie für die benutzerdefinierten Felder jedes Kunden einen benutzerdefinierten Code benötigen.
Andy
@ Andy: Weißt du was? Noch unerreichbarer wird es, wenn Sie tausend verschiedene Schemata in einer einzigen Tabelle kombinieren! Und ja, Sie benötigen wahrscheinlich benutzerdefinierten Code für benutzerdefinierte Felder. Auch das ist einfacher, nicht schwieriger, wenn jeder Kunde einen sauberen, separaten Tisch hat. Der Versuch, die Felder von Firma X unter tausend anderen auszusuchen, ist ein blutiges Durcheinander.
MSalters
Beziehen Sie sich auf meine Antwort oder die Idee des OP, alle zusätzlichen Spalten auf den Kundentisch zu setzen?
Andy
2
Ziel ist es, eine wartbare und skalierbare Lösung zu finden. Das Erstellen einer Tabelle pro Kunde ist definitiv das Gegenteil davon. Jedes Mal, wenn Sie einen neuen Kunden gewinnen, ist es nicht realistisch, ein Skript zum Erstellen einer Tabelle auszuführen, Ihren Code (Entitätsobjekte) zu aktualisieren und erneut bereitzustellen.
TsOverflow
Diese Idee, gemeinsam genutzte Tabellen für alle Kunden zu verwenden, ist selbst eine separate Diskussion zur SaaS-Architektur, und es gibt einige gute Gründe, Kunden in unterschiedlichen Tabellen zu belassen (oder sogar in unterschiedlichen Datenbanken, um Backup / Restore und Skalierung pro Kunde zu ermöglichen). In diesem Szenario ist das Erstellen von cusotm-Spalten in der Haupttabelle ein Kinderspiel. Ich habe zugestimmt, und ich frage mich, warum die Leute dies ablehnen, nur weil sie diesen Ansatz nicht mögen. Der innere Plattformeffekt ist eine Realität:
Wenn Sie
-1

Meine Lösung geht davon aus, dass Sie diese Abfrage von einem Programm aus aufrufen und die Nachbearbeitung durchführen können. Sie können folgende Spalten haben:

ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, CUSTOM_VALUES

CUSTOM_VALUES ist vom Typ string, der das Schlüssel- und Wertepaar speichert. Schlüssel wird Spaltenname sein und Wert wird Spaltenwert sein, z

LIKE_MOVIE;yes;LIKE_MUSIC;no;FAV_CUISINE;rice

In diesem CUSTOM_VALUES werden nur die vorhandenen Informationen gespeichert. Wenn Sie vom Programm abfragen, können Sie diesen String teilen und verwenden.

Ich habe diese Logik verwendet und sie funktioniert einwandfrei. Sie müssen lediglich die Filterlogik im Code und nicht in der Abfrage anwenden.

techExplorer
quelle