Entwerfen einer Datenbankstruktur für Freundschaften: Soll ich eine mehrwertige Spalte verwenden?

9

Angenommen, ich habe eine Tabelle User_FriendListmit den folgenden Eigenschaften:

CREATE TABLE User_FriendList (
    ID ...,
    User_ID...,
    FriendList_IDs...,
    CONSTRAINT User_Friendlist_PK PRIMARY KEY (ID)
);

Nehmen wir an, dass diese Tabelle die folgenden Daten enthält:

 + ---- + --------- + --------------------------- +
 | ID | Benutzer_ID | Friendlist_IDs             |
 + ---- + --------- + --------------------------- +
 | 1 | 102 | 2: 15: 66: 35: 26: 17: |
 + ---- + --------- + --------------------------- +
 | 2 | 114 | 1: 12: 63: 33: 24: 16: 102 |
 + ---- + --------- + --------------------------- +
 | 3 | 117 | 6: 24: 52: 61: 23: 90: 97: 118 |
 + ---- + --------- + --------------------------- +

Hinweis: Das ":" (Doppelpunkt) ist das Trennzeichen, wenn in PHP in ein explodiert wirdarray .

Fragen

So:

  • Ist dies eine bequeme Möglichkeit, das IDsvon a zu „speichern“ FriendList?

  • Oder sollte ich stattdessen einzelne Zeilen mit jeweils nur einem FriendIdWert haben und, wenn ich alle Zeilen einer bestimmten Liste abrufen muss , einfach eine Abfrage wie ausführen SELECT * FROM UserFriendList WHERE UserId = 1?

yeln
quelle
3
Ich empfehle Ihnen, dies zu lesen: stackoverflow.com/questions/3653462/…
Tombom
Es ist eine bequeme Möglichkeit, die IDs zu speichern, solange Sie nicht vorhaben, etwas damit zu tun, und sich nicht besonders um die Datenqualität kümmern.
Mustaccio

Antworten:

19

Verwalten einer einzelnen Information

Angenommen, in Ihrer Geschäftsdomäne

  • Ein Benutzer kann null, eins oder viele Freunde haben .
  • Ein Freund muss zuerst als Benutzer registriert sein . und
  • Sie werden nach einzelnen Werten einer Freundesliste suchen und / oder diese hinzufügen und / oder entfernen und / oder ändern .

dann repräsentiert jedes spezifische Datum, das in der Friendlist_IDsmehrwertigen Spalte gesammelt wird, eine separate Information , die eine sehr genaue Bedeutung hat. Daher die Spalte

  • beinhaltet eine geeignete Gruppe expliziter Einschränkungen, und
  • Seine Werte können durch mehrere relationale Operationen (oder Kombinationen davon) individuell manipuliert werden.

Kurze Antwort

Folglich sollten Sie jeden der Friendlist_IDsWerte in (a) einer Spalte beibehalten , die ausschließlich einen einzigen Wert pro Zeile in (b) einer Tabelle akzeptiert, die den Assoziationstyp auf konzeptioneller Ebene darstellt , der zwischen Benutzern stattfinden kann , dh eine Freundschaft - wie Ich werde in den folgenden Abschnitten veranschaulichen -.

Auf diese Weise werden Sie in der Lage zu handhaben (i) die Tabelle als eine mathematische Beziehung und (ii) die Säule als eine mathematische Beziehung Attribut -wie viel wie MySQL und seine SQL - Dialekt erlauben, von Kurs-.

Warum?

Da das von Dr. E. F. Codd erstellte relationale Datenmodell Tabellen erfordert, die aus Spalten bestehen, die genau einen Wert der zutreffenden Domäne oder des jeweiligen Typs pro Zeile enthalten. Daher stellt die Deklaration einer Tabelle mit einer Spalte, die mehr als einen Wert der betreffenden Domäne oder des fraglichen Typs (1) enthalten kann, keine mathematische Beziehung dar und (2) würde es nicht ermöglichen, die im oben genannten theoretischen Rahmen vorgeschlagenen Vorteile zu erzielen.

Modellieren von Freundschaften zwischen Benutzern : Definieren Sie zuerst die Regeln für die Geschäftsumgebung

Ich empfehle dringend, eine Datenbank zu erstellen, die - vor allem - das entsprechende konzeptionelle Schema aufgrund der Definition der relevanten Geschäftsregeln abgrenzt , die unter anderem die Arten von Wechselbeziehungen beschreiben müssen, die zwischen den verschiedenen interessierenden Aspekten bestehen, d. H. , die anwendbaren Entitätstypen und ihre Eigenschaften ; z.B:

  • Ein Benutzer wird hauptsächlich durch seine Benutzer- ID identifiziert
  • Ein Benutzer wird abwechselnd durch die Kombination seines Vornamens , Nachnamens , Geschlechts und Geburtsdatums identifiziert
  • Ein Benutzer wird abwechselnd durch seinen Benutzernamen identifiziert
  • Ein Benutzer ist der Antragsteller von null-eins-oder-vielen Freundschaften
  • Ein Benutzer ist der Adressat von null, eins oder vielen Freundschaften
  • Eine Freundschaft wird hauptsächlich durch die Kombination ihrer RequesterId und ihrer AddresseeId identifiziert

Expository IDEF1X-Diagramm

Auf diese Weise konnte ich das in Abbildung 1 gezeigte IDEF1X 1- Diagramm ableiten , das die meisten zuvor formulierten Regeln integriert:

Abbildung 1. IDEF1X-Diagramm für Benutzerfreundschaften

Wie dargestellt, sind Anforderer und Adressat Bezeichnungen, die die Rollen ausdrücken, die von den spezifischen Benutzern ausgeführt werden , die an einer bestimmten Freundschaft teilnehmen .

Unter diesen Umständen zeigt der Entitätstyp " Freundschaft" einen Assoziationstyp mit einem Kardinalitätsverhältnis von vielen zu vielen (M: N), der verschiedene Vorkommen desselben Entitätstyps, dh Benutzer, umfassen kann . Als solches ist es ein Beispiel für das klassische Konstrukt, das als "Stückliste" oder "Teileexplosion" bekannt ist.


1 Integration Definition für Information Modeling ( IDEF1X ) ist eine sehr empfehlenswerte Technikdie als etabliert wurde Standard im Dezember 1993 von der US National Institute of Standards and Technology (NIST). Es basiert fest auf (a) dem frühen theoretischen Material, das vom alleinigen Urheber des relationalen Modells verfasst wurde, dh Dr. EF Codd ; zu (b) dervon Dr. PP Chen entwickelten Entity-Relationship- Ansicht von Daten; und auch auf (c) der Logical Database Design Technique, erstellt von Robert G. Brown.


Illustratives logisches SQL-DDL-Design

Aus dem oben dargestellten IDEF1X-Diagramm ist es dann viel „natürlicher“, eine DDL-Anordnung wie die folgende zu deklarieren:

-- You should determine which are the most fitting 
-- data types and sizes for all the table columns 
-- depending on your business context characteristics.

-- At the physical level, you should make accurate tests 
-- to define the mostconvenient INDEX strategies based on 
-- the pertinent query tendencies.

-- As one would expect, you are free to make use of 
-- your preferred (or required) naming conventions. 

CREATE TABLE UserProfile ( -- Represents an independent entity type.
    UserId          INT      NOT NULL,
    FirstName       CHAR(30) NOT NULL,
    LastName        CHAR(30) NOT NULL,
    BirthDate       DATE     NOT NULL,
    GenderCode      CHAR(3)  NOT NULL,
    Username        CHAR(20) NOT NULL,
    CreatedDateTime DATETIME NOT NULL,
    --
    CONSTRAINT UserProfile_PK  PRIMARY KEY (UserId),
    CONSTRAINT UserProfile_AK1 UNIQUE ( -- Composite ALTERNATE KEY.
        FirstName,
        LastName,
        GenderCode,
        BirthDate
    ),
    CONSTRAINT UserProfile_AK2 UNIQUE (Username) -- Single-column ALTERNATE KEY.
);

CREATE TABLE Friendship ( -- Stands for an associative entity type.
    RequesterId     INT      NOT NULL,
    AddresseeId     INT      NOT NULL, -- Fixed with a well-delimited data type.
    CreatedDateTime DATETIME NOT NULL,
    --
    CONSTRAINT Friendship_PK            PRIMARY KEY (RequesterId, AddresseeId), -- Composite PRIMARY KEY.
    CONSTRAINT FriendshipToRequester_FK FOREIGN KEY (RequesterId)
        REFERENCES UserProfile (UserId),
    CONSTRAINT FriendshipToAddressee_FK FOREIGN KEY (AddresseeId)
        REFERENCES UserProfile (UserId)
);

In dieser Mode:

  • Jede Basistabelle repräsentiert einen einzelnen Entitätstyp.
  • Jede Spalte steht für eine alleinige Eigenschaft des jeweiligen Entitätstyps.
  • Für jede Spalte wird ein bestimmter Datentyp a festgelegt , um sicherzustellen, dass alle darin enthaltenen Werte zu einer bestimmten und genau definierten Menge gehören , sei es INT, DATETIME, CHAR usw.; und
  • Mehrere Einschränkungen b werden (deklarativ) konfiguriert, um sicherzustellen, dass die Zusicherungen in Form von Zeilen, die in allen Tabellen beibehalten werden , den im konzeptionellen Schema festgelegten Geschäftsregeln entsprechen.

Vorteile einer einwertigen Spalte

Wie gezeigt, können Sie z.

  • Nutzen Sie die referenzielle Integrität, die vom Datenbankverwaltungssystem (DBMS für die Kürze) für die Friendship.AddresseeIdSpalte erzwungen wird , da die Einschränkung als AUSLÄNDISCHER SCHLÜSSEL (FK für die Kürze), der auf die UserProfile.UserIdSpalte verweist, garantiert, dass jeder Wert auf eine vorhandene Zeile verweist .

  • Erstellen Sie einen zusammengesetzten PRIMARY KEY (PK), der aus der Kombination von Spalten besteht (Friendship.RequesterId, Friendship.AddresseeId)und dabei hilft, alle eingefügten Zeilen elegant zu unterscheiden und natürlich ihre Eindeutigkeit zu schützen .

    Dies bedeutet natürlich, dass das Anhängen einer zusätzlichen Spalte für vom System zugewiesene Ersatzwerte (z. B. eine Spalte, die mit der IDENTITY- Eigenschaft in Microsoft SQL Server oder mit dem AUTO_INCREMENT- Attribut in MySQL eingerichtet wurde) und dem unterstützenden INDEX völlig überflüssig ist .

  • Beschränken Sie die beibehaltenen Werte Friendship.AddresseeIdauf einen genauen Datentyp c (der z. B. dem für UserProfile.UserIdINT festgelegten Datentyp entsprechen sollte ), damit das DBMS die entsprechende automatische Validierung übernimmt .

    Dieser Faktor kann auch dazu beitragen, (a) die entsprechenden integrierten Typfunktionen zu nutzen und (b) die Speicherplatznutzung zu optimieren.

  • Optimieren Daten Retrieval auf der physikalischen Ebene durch die Konfiguration kleinen und schneller untergeordneten Indizes für die Friendship.AddresseeIdSpalte, da diese physikalischen Elemente im wesentlichen durch in unterstützen die Beschleunigung der Abfragen , die Spalte sagte beinhalten.

    Natürlich können Sie beispielsweise einen einspaltigen INDEX für sich Friendship.AddresseeIdallein erstellen , einen mehrspaltigen, der Friendship.RequesterIdund Friendship.AddresseeIdoder beides umfasst.

  • Vermeiden Sie die unnötige Komplexität, die durch das „Suchen“ nach bestimmten Werten entsteht, die in derselben Spalte zusammengefasst sind (sehr wahrscheinlich dupliziert, falsch eingegeben usw.). Diese Vorgehensweise würde möglicherweise die Funktion Ihres Systems verlangsamen, weil Sie dies tun würden müssen auf ressourcen- und zeitaufwändige nicht relationale Methoden zurückgreifen, um diese Aufgabe zu erfüllen.

Es gibt mehrere Gründe, die eine sorgfältige Analyse des relevanten Geschäftsumfelds erfordern, um den Typ d jeder Tabellenspalte genau zu kennzeichnen .

Wie bereits erläutert, ist die Rolle des Datenbankdesigners von größter Bedeutung, um (1) die Vorteile des relationalen Modells auf logischer Ebene und (2) die vom DBMS der Wahl bereitgestellten physikalischen Mechanismen optimal zu nutzen .


a , b , c , d Offensichtlich können Sie bei der Arbeit mit SQL-Plattformen (z. B. Firebird und PostgreSQL ), die die DOMAIN-Erstellung unterstützen (eine besondere relationale Funktion), Spalten deklarieren, die nur Werte akzeptieren, die zu ihren jeweiligen gehören (entsprechend eingeschränkt und manchmal) geteilt) DOMAINs.


Ein oder mehrere Anwendungsprogramme, die die betreffende Datenbank gemeinsam nutzen

Wenn Sie arraysden Code der Anwendungsprogramme verwenden müssen, die auf die Datenbank zugreifen, müssen Sie lediglich die relevanten Datensätze vollständig abrufen und sie dann an die betreffende Codestruktur „binden“ oder ausführen zugehörige App-Prozesse, die stattfinden sollen.

Weitere Vorteile einwertiger Spalten: Datenbankstrukturerweiterungen sind viel einfacher

Ein weiterer Vorteil des Haltens des AddresseeIdDatenpunkts in seiner reservierten und ordnungsgemäß typisierten Spalte besteht darin, dass die Erweiterung der Datenbankstruktur erheblich erleichtert wird, wie ich weiter unten veranschaulichen werde.

Fortschreiten des Szenarios: Einbeziehung des Friendship Status- Konzepts

Da sich Freundschaften im Laufe der Zeit entwickeln können, müssen Sie möglicherweise ein solches Phänomen im Auge behalten. Daher müssten Sie (i) das konzeptionelle Schema erweitern und (ii) einige weitere Tabellen im logischen Layout deklarieren. Lassen Sie uns also die nächsten Geschäftsregeln festlegen, um die neuen Eingliederungen abzugrenzen:

  • Eine Freundschaft enthält eins zu viele FriendshipStatuses
  • Ein FriendshipStatus wird hauptsächlich durch die Kombination seiner RequesterId , seiner AddresseeId und seiner SpecifiedDateTime identifiziert
  • Ein Benutzer gibt null, eins oder viele FriendshipStatuses an
  • Ein Status klassifiziert null, eins oder viele FriendshipStatuses
  • Ein Status wird hauptsächlich durch seinen StatusCode identifiziert
  • Ein Status wird abwechselnd durch seinen Namen identifiziert

Erweitertes IDEF1X-Diagramm

Nacheinander kann das vorherige IDEF1X-Diagramm erweitert werden, um die oben beschriebenen neuen Entitätstypen und Wechselbeziehungstypen einzuschließen. Ein Diagramm mit den vorherigen Elementen, die den neuen Elementen zugeordnet sind, ist in Abbildung 2 dargestellt :

Abbildung 2. IDEF1X-Diagramm zum Freundschaftsstatus

Logische Strukturergänzungen

Anschließend können wir das DDL-Layout mit den folgenden Deklarationen verlängern:

--
CREATE TABLE MyStatus ( -- Denotes an independent entity type.
    StatusCode CHAR(1)  NOT NULL,
    Name       CHAR(30) NOT NULL,
    --
    CONSTRAINT MyStatus_PK PRIMARY KEY (StatusCode),
    CONSTRAINT MyStatus_AK UNIQUE      (Name) -- ALTERNATE KEY.
); 

CREATE TABLE FriendshipStatus ( -- Represents an associative entity type.
    RequesterId       INT      NOT NULL,
    AddresseeId       INT      NOT NULL,
    SpecifiedDateTime DATETIME NOT NULL,
    StatusCode        CHAR(1)  NOT NULL,
    SpecifierId       INT      NOT NULL,
    --
    CONSTRAINT FriendshipStatus_PK             PRIMARY KEY (RequesterId, AddresseeId, SpecifiedDateTime), -- Composite PRIMARY KEY.
    CONSTRAINT FriendshipStatusToFriendship_FK FOREIGN KEY (RequesterId, AddresseeId)
        REFERENCES Friendship  (RequesterId, AddresseeId), -- Composite FOREIGN KEY.
    CONSTRAINT FriendshipStatusToMyStatus_FK   FOREIGN KEY (StatusCode)
        REFERENCES MyStatus    (StatusCode),
    CONSTRAINT FriendshipStatusToSpecifier_FK  FOREIGN KEY (SpecifierId)
        REFERENCES UserProfile (UserId)      
);

Folglich müssten die Benutzer jedes Mal, wenn der Status einer bestimmten Freundschaft auf den neuesten Stand gebracht werden muss, nur eine neue Zeile einfügen , die Folgendes enthält:FriendshipStatus

  • die geeigneten RequesterIdund AddresseeIdWerte - entnommen aus der betreffenden FriendshipZeile -;

  • der neue und bedeutungsvolle StatusCodeWert - gezogen von MyStatus.StatusCode-;

  • der genaue INSERTion-Zeitpunkt, dh - SpecifiedDateTimevorzugsweise mithilfe einer Serverfunktion, damit Sie sie zuverlässig abrufen und aufbewahren können -; und

  • Der SpecifierIdWert, der den jeweiligen Wert angibt, der UserIddas Neue FriendshipStatusin das System eingegeben hat - idealerweise mithilfe Ihrer App-Funktionen.

Nehmen wir insofern an, dass die MyStatusTabelle die folgenden Daten enthält - mit PK-Werten, die (a) Endbenutzer-, App-Programmierer- und DBA-freundlich und (b) klein und schnell in Bezug auf Bytes auf der Ebene der physischen Implementierung sind -:

 + -——————————- + -—————————- +
 | StatusCode | Name       |
 + -——————————- + -—————————- +
 | R | Angefordert |
 + ------------ + ----------- +
 | A | Akzeptiert |
 + ------------ + ----------- +
 | D | Abgelehnt |
 + ------------ + ----------- +
 | B | Bloqued |
 + ------------ + ----------- +

Die FriendshipStatusTabelle kann also Daten wie unten gezeigt enthalten:

 + - ———- + -——————————- + -———————————- +
 | RequesterId | AddresseeId | SpecifiedDateTime        | StatusCode | SpecifierId |
 + - ———- + -——————————- + -———————————- +
 | 1750 | 1748 | 2016-04-01 16: 58: 12.000 | R | 1750 |
 + ------------- + ------------- + --------------------- ---- + ------------ + ------------- +
 | 1750 | 1748 | 2016-04-02 09: 12: 05.000 | A | 1748 |
 + ------------- + ------------- + --------------------- ---- + ------------ + ------------- +
 | 1750 | 1748 | 2016-04-04 10: 57: 01.000 | B | 1750 |
 + ------------- + ------------- + --------------------- ---- + ------------ + ------------- +
 | 1750 | 1748 | 2016-04-07 07: 33: 08.000 | R | 1748 |
 + ------------- + ------------- + --------------------- ---- + ------------ + ------------- +
 | 1750 | 1748 | 2016-04-08 12: 12: 09.000 | A | 1750 |
 + ------------- + ------------- + --------------------- ---- + ------------ + ------------- +

Wie Sie sehen können, kann gesagt werden, dass die FriendshipStatusTabelle dem Zweck dient, eine Zeitreihe zu umfassen .


Relevante Beiträge

Sie könnten genauso gut interessiert sein an:

  • Diese Antwort, in der ich eine grundlegende Methode vorschlage, um mit einer gemeinsamen Viele-zu-Viele-Beziehung zwischen zwei unterschiedlichen Entitätstypen umzugehen.
  • Das in Abbildung 1 gezeigte IDEF1X-Diagramm, das diese andere Antwort veranschaulicht . Achten Sie besonders auf die Entitätstypen " Ehe" und " Nachkommen" , da dies zwei weitere Beispiele für den Umgang mit dem "Teileexplosionsproblem" sind.
  • Dieser Beitrag enthält eine kurze Überlegung zum Speichern verschiedener Informationen in einer einzelnen Spalte.
MDCCL
quelle