5 Stimmen

Versuch, "polymorphe Assoziationen" zu vermeiden und die referentielle Integrität von Fremdschlüsseln zu wahren

Ich baue eine Website auf, die Yelp ähnelt (Recommendation Engine, allerdings in kleinerem Maßstab), daher wird es drei Hauptkomponenten im System geben: Benutzer, Ort (einschließlich Unternehmen) und Ereignis.

Nun frage ich mich, wie man Informationen wie Fotos, Kommentare und "Komplimente" (ähnlich wie Facebooks "Gefällt mir") für jede dieser Arten von Entitäten und auch für jedes Objekt, auf das sie angewendet werden können (z. B. Kommentar zu einer Empfehlung, Foto usw.) speichern kann. Bisher hatte ich für jedes Objekt eine eigene Tabelle, d. h.

Foto (id, Typ , Eigentümer_id , is_main, etc...)
wobei Typ für steht: 1=Benutzer, 2=Ort, 3=Ereignis

Kommentar (id, object_type, object_id , user_id, Inhalt, etc, etc...)
wobei object_type für verschiedene Objekte wie Fotos, Empfehlungen usw. stehen kann

Kompliment ( objekt_id , Objekt_Typ , compliment_type, user_id)
wobei object_type für verschiedene Objekte wie Fotos, Empfehlungen usw. stehen kann

Aktivität (id, Quelle, Quelle_Typ , source_id , etc..) //for "activity feed"
wobei source_type ein Benutzer, ein Ort oder ein Ereignis ist

Benachrichtigung (id, Empfänger, Absender, activity_type, Objekt_Typ , objekt_id , etc...)
wobei object_type & object_id verwendet werden, um einen direkten Link zum Objekt der Benachrichtigung zu erstellen, z. B. das Foto eines Nutzers, der ein Kompliment erhalten hat

Aber nach Lesen ein paar Beiträge Bei SO wurde mir klar, dass ich die referentielle Integrität nicht mit einem Fremdschlüssel aufrechterhalten kann, da dies eine 1:1-Beziehung erfordert und meine Felder source_id/object_id sich auf eine ID in mehr als einer Tabelle beziehen können. Also entschied ich mich für die Methode, die Hauptentität beizubehalten, sie aber in Teilmengen aufzuteilen, d. h.

Benutzerfoto (photo_id, user_id) | Ortsfoto (photo_id, place_id) | etc...

Photo_Comment (comment_id, photo_id) | Recommendation_Comment(comment_id, rec_id) | etc...

Kompliment (id, ...) //would need to add a surrogate key to Compliment table now

Foto_Kompliment(compliment_id, photo_id) | Kommentar_Kompliment(compliment_id, comment_id) | usw...

User_Activity(activity_id, user_id) | Place_Activity(activity_id, place_id) | etc...

Ich dachte, ich könnte einfach Ansichten erstellen, die jede Untertabelle mit der Haupttabelle verbinden, um die gewünschten Ergebnisse zu erhalten. Außerdem denke ich, es würde auch in meine Objektmodelle in Code Igniter passen.

Die einzige Tabelle, von der ich denke, dass ich sie stehen lassen könnte, ist die Tabelle mit den Benachrichtigungen, da es viele Objekttypen gibt (Forenbeitrag, Foto, Empfehlung usw.), und diese Tabelle enthält die Benachrichtigungen ohnehin nur für eine Woche, so dass etwaige Probleme mit der Integrität von Referenzen kein großes Problem darstellen sollten (denke ich).

Gehe ich also auf eine vernünftige Art und Weise vor? Gibt es Probleme mit der Leistung, der Zuverlässigkeit oder andere Probleme, die ich vielleicht übersehen habe?

Das einzige "Problem", das ich sehe, ist, dass ich am Ende eine Menge Tische haben würde (im Moment habe ich etwa 72, also schätze ich, dass ich am Ende etwas weniger als 90 Tische haben würde, wenn ich die Extras hinzufüge), und das ist kein Problem, soweit ich das beurteilen kann.

Für jede Art von Feedback bin ich sehr dankbar. Vielen Dank im Voraus.

EDITAR : Nur um das klarzustellen, ich mache mir keine Sorgen, wenn ich am Ende 10 oder so Tische mehr habe. Von dem, was ich weiß, die Anzahl der Tabellen ist nicht zu viel von einem Problem (sobald sie verwendet werden)... es sei denn, Sie hatten sagen 200 oder so :/

6voto

Damir Sudarevic Punkte 21527

Einige Vorschläge für dieses UoD (Universum des Diskurses)

  • Ein Benutzer namens Bob hat sich angemeldet.
  • Der Benutzer Bob hat das Foto Nummer 56 hochgeladen.
  • Es gibt einen Ort namens London.
  • Das Foto Nummer 56 zeigt einen Ort namens London.
  • Der Benutzer Joe hat den Kommentar "sehr schön" zum Foto Nr. 56 erstellt.

So führen Sie Objekt-IDs ein

  • Benutzer (UserID) eingeloggt.
  • Benutzer (UserID) hat Foto (PhotoID) hochgeladen.
  • Es gibt einen Ort (PlaceID).
  • Das Foto (PhotoID) gehört zum Ort (PlaceID).
  • Benutzer (UserID) hat Kommentar (CommentID) zu Foto (PhotoID) erstellt.

Nur Fakten-Typen

  • Eingeloggter Benutzer.
  • Benutzer hochgeladenes Foto.
  • Ort existiert.
  • Das Foto ist von Place.
  • Benutzer erstellt Kommentar zu Foto.

Jetzt zu extrahieren Prädikate

Predicate               Predicate Arity
---------------------------------------------
... logged in            1 (Unary predicate)
... uploaded ...         2 (Binary)
... exists               1 (Unary) 
... is of ...            2 (Binary)
... created ... on ...   3 (Ternary)

Es sieht so aus, als ob jeder Vorschlag ist diese UoD kann mit max ternär angegeben werden Prädikat , Ich würde also etwas vorschlagen wie

enter image description here

Prädikat Rolle (Role_1_ID, Role_2_ID, Role_3_ID) ist eine Rolle, die ein Objekt in einem Prädikat spielt. Ersetzen Sie die ... in einem Prädikat von links nach rechts mit jedem Role_ID . Beachten Sie, dass nur Role_1_ID ist obligatorisch (mindestens unäres Prädikat), die beiden anderen können NULL sein.

In diesem einfachen Modell ist es möglich vorschlagen alles. Daher müssten Sie Beschränkungen für den Anwendungsschicht . Sie müssen zum Beispiel sicherstellen, dass es möglich ist, Folgendes zu erstellen Comment en Place , aber nicht erstellen Place en Place . Nicht alle Prädikate steht für eine Aktion, zum Beispiel ... logged in ist eine Aktion, während ... is of ... nicht ist. Also, Ihr Aktivitäts-Feed würde alle Propositions con Predicate.IsAction = True .

3voto

Joel Brown Punkte 13792

Wenn Sie die Dinge etwas umstellen, können Sie Ihre Kommentare und Komplimente vereinfachen. Im Wesentlichen möchten Sie einen einzigen Speicher für Kommentare und einen weiteren für Komplimente haben. Ihr Problem ist, dass Sie damit keine deklarative referenzielle Integrität (Fremdschlüssel-Beschränkungen) verwenden können.

Das Problem lässt sich dadurch lösen, dass die Objekte, die Kommentare und Komplimente erhalten können, alle logische Untertypen eines Supertyps sind. Aus logischer Sicht bedeutet dies, dass Sie eine "THING_OF_INTEREST"-Entität haben (ich bin no hier eine Empfehlung zur Namensgebung abzugeben!) und jedes der verschiedenen spezifischen Dinge, die Kommentare und Komplimente anziehen, wird ein Untertyp von THING_OF_INTEREST sein. Daher wird Ihre Kommentartabelle eine FK-Spalte "Ding_von_Interesse_id" haben, und das Gleiche gilt für Ihre Komplimentetabelle. Sie werden immer noch die Untertabellen haben, aber sie werden eine 1:1 FK mit THING_OF_INTEREST haben. Mit anderen Worten, THING_OF_INTEREST hat die Aufgabe, Ihnen eine einzige Primärschlüsseldomäne zu geben, während alle Subtyp-Tabellen die typspezifischen Attribute enthalten. Auf diese Weise können Sie immer noch die deklarative referentielle Integrität verwenden, um Ihre Beziehungen zwischen Kommentaren und Komplimenten durchzusetzen, ohne dass Sie separate Tabellen für verschiedene Arten von Kommentaren und Komplimenten haben müssen.

Von einem physisch Durchführungsperspektive ist das Wichtigste, dass Ihre verschiedenen Dinge von Interesse alle einen gemeinsamen Primärschlüsselbereich haben. Dadurch hat Ihre Kommentartabelle einen einzigen FK-Wert, der leicht mit dem jeweiligen Gegenstand von Interesse verknüpft werden kann.

Je nachdem, wie Sie nach Ihren Kommentaren und Empfehlungen vorgehen, müssen Sie wahrscheinlich (aber nicht unbedingt) THING_OF_INTEREST physisch implementieren - das mindestens zwei Attribute haben wird, den Primärschlüssel (in der Regel ein int) plus ein Partitionierungsattribut, das Ihnen sagt, welcher Untertyp von Ding es ist.

2voto

Marius Burz Punkte 4455

Wenn Sie referential integrity (RI) gibt es keinen besseren Weg als die Verwendung von Many-to-Many-Verbindungstabellen. Es stimmt zwar, dass man am Ende viele Tabellen im System hat, aber das ist der Preis, den man zahlen muss. Es hat auch einige andere Vorteile, wenn man diesen Weg geht, zum Beispiel bekommt man eine Art von Partitionierung umsonst: man bekommt die Daten partitioniert nach ihrem Beziehungstyp, jede in ihrer eigenen Tabelle. Dies bietet RI, ist aber auch nicht 100 % sicher. Es gibt zum Beispiel keine Garantie dafür, dass ein Kommentar zu einem Foto und nur zu diesem Foto gehört; Sie müssen diese Art von Einschränkungen manuell erzwingen, wenn Sie sie brauchen.

Auf der anderen Seite, mit einer generischen Lösung, wie Sie bereits getan haben, erhalten Sie schneller aus dem Boden und es ist viel einfacher, in der Zukunft zu erweitern, aber es wird keine RI, es sei denn, Sie werden es manuell Code (was sehr komplex und viel schwieriger zu behandeln als die Alternative M:M für jede Beziehung Typ).

Um eine weitere Alternative zu erwähnen: Ähnlich wie bei Ihrer bestehenden Implementierung könnten Sie eine benutzerdefinierte M:M-Kreuzungstabelle verwenden, um alle Ihre Beziehungen unabhängig von ihrem Typ zu behandeln: object1_type, object1_id, object2_type, object2_id . Einfach, aber kein weiterer Nutzen neben sehr einfach zu implementieren und zu erweitern. Ich würde es nur empfehlen, wenn Sie RI nicht brauchen und Sie haben eine Menge von Tabellen, die alle miteinander verbunden sind.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X