Conception d'une structure de base de données d'amitiés: Dois-je utiliser une colonne à plusieurs valeurs?

9

Disons que j'ai une table appelée User_FriendList, qui présente les caractéristiques suivantes:

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

Et supposons que cette table contient les données suivantes:

 + ---- + --------- + --------------------------- +
 | ID | ID_utilisateur | 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 |
 + ---- + --------- + --------------------------- +

Remarque: Le «:» (deux-points) est le délimiteur lors de l' explosion en PHP dans un array.

Des questions

Donc:

  • Est-ce un moyen pratique de «stocker» IDsun FriendList?

  • Ou, au lieu de cela, dois-je avoir des lignes individuelles avec une seule FriendIdvaleur dans chacune d'elles et, lorsque j'ai besoin de récupérer toutes les lignes d'une liste donnée , il suffit d'effectuer une requête comme SELECT * FROM UserFriendList WHERE UserId = 1?

yeln
la source
3
Je vous recommande de lire ceci: stackoverflow.com/questions/3653462/…
tombom
C'est un moyen quelque peu pratique de stocker les ID, tant que vous ne prévoyez rien faire avec eux et que vous ne vous souciez pas particulièrement de la qualité des données.
mustaccio
Je pense que codedodle.com/2014/12/social-network-friends-database.html pourrait être l'une des meilleures solutions.
Gupta

Réponses:

19

Gérer une information individuelle

En supposant que, dans votre domaine d'activité,

  • un utilisateur peut avoir zéro ou un ou plusieurs amis ;
  • un ami doit d'abord être enregistré en tant qu'utilisateur ; et
  • vous rechercherez et / ou ajouterez et / ou supprimerez et / ou modifierez les valeurs uniques d'une liste d'amis ;

puis chaque donnée spécifique collectée dans la Friendlist_IDscolonne à plusieurs valeurs représente une information distincte qui a une signification très exacte. Par conséquent, ladite colonne

  • implique un groupe approprié de contraintes explicites, et
  • ses valeurs ont le potentiel d'être manipulées individuellement au moyen de plusieurs opérations relationnelles (ou de leurs combinaisons).

Réponse courte

Par conséquent, vous devez conserver chacune des Friendlist_IDsvaleurs dans (a) une colonne qui accepte exclusivement une seule valeur par ligne dans (b) un tableau qui représente le type d'association au niveau conceptuel qui peut avoir lieu entre les utilisateurs , c'est-à-dire une amitié - comme Je vais illustrer dans les sections suivantes—.

De cette façon, vous pourrez gérer (i) ladite table comme une relation mathématique et (ii) ladite colonne comme un attribut de relation mathématique - autant que MySQL et son dialecte SQL le permettent, bien sûr -.

Pourquoi?

Parce que le modèle relationnel de données , créé par Dr. E. F. Codd , exige d'avoir des tables qui sont composées de colonnes qui contiennent exactement une valeur du domaine ou du type applicable par ligne; par conséquent, déclarer un tableau avec une colonne qui peut contenir plus d'une valeur du domaine ou du type en question (1) ne représente pas une relation mathématique et (2) ne permettrait pas d'obtenir les avantages proposés dans le cadre théorique susmentionné.

Modélisation des amitiés entre utilisateurs : définir d'abord les règles de l'environnement commercial

Je recommande fortement de commencer à façonner une base de données délimitant - avant toute autre chose - le schéma conceptuel correspondant en vertu de la définition des règles commerciales pertinentes qui, entre autres facteurs, doivent décrire les types d'interrelations qui existent entre les différents aspects d'intérêt, à savoir , les types d'entités applicables et leurs propriétés ; par exemple:

  • Un utilisateur est principalement identifié par son UserId
  • Un utilisateur est alternativement identifié par la combinaison de son FirstName , LastName , Sexe et Date de naissance
  • Un utilisateur est alternativement identifié par son nom d' utilisateur
  • Un utilisateur est le demandeur d' amitiés zéro à un ou plusieurs
  • Un utilisateur est le destinataire d' amitiés nulles
  • Une amitié est principalement identifiée par la combinaison de son RequesterId et de son AddresseeId

Diagramme expositoire IDEF1X

De cette manière, j'ai pu dériver le diagramme IDEF1X 1 illustré à la figure 1 , qui intègre la plupart des règles formulées précédemment:

Figure 1. Diagramme IDEF1X des amitiés des utilisateurs

Comme représenté, le demandeur et le destinataire sont des dénotations qui expriment les rôles effectués par les utilisateurs spécifiques qui participent à une amitié donnée .

Cela étant, le type d'entité Amitié représente un type d'association de ratio de cardinalité plusieurs à plusieurs (M: N) qui peut impliquer différentes occurrences du même type d'entité, c'est-à-dire Utilisateur . À ce titre, il s'agit d'un exemple de la construction classique connue sous le nom de «nomenclature» ou «explosion de pièces».


1 La définition d'intégration pour la modélisation de l'information ( IDEF1X ) est une technique hautement recommandable qui a été établie en tant que norme en décembre 1993 par le National Institute of Standards and Technology (NIST)des États-Unis. Il est solidement fondé sur (a) les premiers éléments théoriques rédigés par le seul auteur du modèle relationnel, c'est-à-dire le Dr EF Codd ; sur (b) la vue entité-relation des données, développée par le Dr PP Chen ; et également sur (c) la technique de conception de bases de données logiques, créée par Robert G. Brown.


Conception logique illustrative SQL-DDL

Ensuite, à partir du diagramme IDEF1X présenté ci-dessus, déclarer un arrangement DDL comme celui qui suit est beaucoup plus «naturel»:

-- 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)
);

Dans cette mode:

  • chaque table de base représente un type d'entité individuel;
  • chaque colonne représente une propriété unique du type d'entité respectif ;
  • un spécifiques type de données une est fixée pour chaque colonne afin de garantir que toutes les valeurs qu'elle contient appartiennent à un particulier et bien défini ensemble , que ce soit INT, DATETIME, CHAR, etc .; et
  • plusieurs contraintes b sont configurées (de manière déclarative) afin de garantir que les assertions sous forme de lignes conservées dans toutes les tables respectent les règles métier déterminées au niveau du schéma conceptuel.

Avantages d'une colonne à valeur unique

Comme démontré, vous pouvez, par exemple:

  • Profitez de l' intégrité référentielle imposée par le système de gestion de base de données (SGBD pour la brièveté) pour la Friendship.AddresseeIdcolonne, car la contraindre en tant que CLÉ ÉTRANGÈRE (FK pour la brièveté) qui fait référence à la UserProfile.UserIdcolonne garantit que chaque valeur pointe vers une ligne existante .

  • Créez une CLÉ PRIMAIRE composite (PK) composée de la combinaison de colonnes (Friendship.RequesterId, Friendship.AddresseeId), aidant à distinguer avec élégance toutes les lignes INSÉRÉES et, naturellement, à protéger leur unicité .

    Bien sûr, cela signifie que l'attachement d'une colonne supplémentaire pour les valeurs de substitution affectées par le système (par exemple, une configuration avec la propriété IDENTITY dans Microsoft SQL Server ou avec l' attribut AUTO_INCREMENT dans MySQL) et l'aide INDEX est entièrement superflue .

  • Limitez les valeurs retenues Friendship.AddresseeIdà un type de données précis c (qui doit correspondre, par exemple, à celui établi pour UserProfile.UserId, dans ce cas INT), en laissant le SGBD se charger de la validation automatique pertinente .

    Ce facteur peut également aider à (a) utiliser les fonctions de type intégrées correspondantes et (b) optimiser l' utilisation de l' espace disque .

  • Optimisez la récupération des données au niveau physique en configurant des INDEX subordonnés petits et rapides pour la Friendship.AddresseeIdcolonne, car ces éléments physiques peuvent considérablement aider à accélérer les requêtes qui impliquent ladite colonne.

    Certes, vous pouvez, par exemple, mettre en place un INDEX à une seule colonne pour Friendship.AddresseeIdseul, un INDEX à plusieurs colonnes qui englobe Friendship.RequesterIdet Friendship.AddresseeId, ou les deux.

  • Évitez la complexité inutile introduite par la «recherche» de valeurs distinctes qui sont collectées ensemble dans la même colonne (très probablement dupliquées, mal tapées, etc.), un plan d'action qui finirait par ralentir le fonctionnement de votre système, car vous le feriez doivent recourir à des méthodes non relationnelles consommatrices de temps et de ressources pour accomplir ladite tâche.

Ainsi, il existe plusieurs raisons qui nécessitent d'analyser attentivement l'environnement commercial pertinent afin de marquer avec précision le type d de chaque colonne de tableau.

Comme expliqué, le rôle joué par le concepteur de base de données est primordial pour tirer le meilleur parti (1) des avantages de niveau logique offerts par le modèle relationnel et (2) des mécanismes physiques fournis par le SGBD de choix.


a , b , c , d Evidemment, lorsque vous travaillez avec des plates-formes SQL (par exemple, Firebird et PostgreSQL ) qui prennent en charge la création de DOMAIN (une caractéristique relationnelle distincte), vous pouvez déclarer des colonnes qui n'acceptent que des valeurs qui appartiennent à leurs respectives (convenablement contraintes et parfois DOMAINs partagés).


Un ou plusieurs programmes d'application partageant la base de données considérée

Lorsque vous devez utiliser arraysle code du ou des programmes d'application accédant à la base de données, il vous suffit de récupérer l' intégralité du ou des jeux de données pertinents , puis de les «lier» à la structure de code concernée ou d'exécuter le processus (s) d'application associés qui devraient avoir lieu.

Autres avantages des colonnes à valeur unique: les extensions de structure de base de données sont beaucoup plus faciles

Un autre avantage du maintien du AddresseeIdpoint de données dans sa colonne réservée et correctement typée est qu'il facilite considérablement l'extension de la structure de la base de données, comme je le montrerai ci-dessous.

Progression du scénario: intégration du concept de statut d'amitié

Étant donné que les amitiés peuvent évoluer au fil du temps, vous devrez peut-être suivre un tel phénomène, vous devrez donc (i) étendre le schéma conceptuel et (ii) déclarer quelques tables supplémentaires dans la disposition logique. Alors, organisons les prochaines règles métier pour délimiter les nouvelles incorporations:

  • Une amitié tient une-à-plusieurs FriendshipStatuses
  • Un FriendshipStatus est principalement identifié par la combinaison de son RequesterId , de son AddresseeId et de son SpecifiedDateTime
  • Un utilisateur spécifie zéro ou un ou plusieurs FriendshipStatuses
  • Un statut classe zéro à un ou plusieurs FriendshipStatuses
  • Un statut est principalement identifié par son StatusCode
  • Un statut est alternativement identifié par son nom

Diagramme IDEF1X étendu

Successivement, le diagramme IDEF1X précédent peut être étendu afin d'inclure les nouveaux types d'entité et les types d'interrelation décrits ci-dessus. Un diagramme décrivant les éléments précédents associés aux nouveaux est présenté à la figure 2 :

Figure 2. Diagramme IDEF1X du statut d'amitié

Ajouts de structure logique

Ensuite, nous pouvons allonger la disposition DDL avec les déclarations suivantes:

--
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)      
);

Par conséquent, chaque fois que le statut d'une amitié donnée doit être mis à jour, les utilisateurs n'auront qu'à INSÉRER une nouvelle FriendshipStatusligne, contenant:

  • les valeurs appropriées RequesterIdet - AddresseeIdtirées de la ligne concernée Friendship-;

  • la StatusCodevaleur nouvelle et significative - tirée de MyStatus.StatusCode-;

  • l'instant INSERTion exact, c'est-à-dire SpecifiedDateTime- en utilisant de préférence une fonction serveur afin que vous puissiez la récupérer et la conserver de manière fiable -; et

  • la SpecifierIdvaleur qui indiquerait le respectif UserIdqui a entré le nouveau FriendshipStatusdans le système —idéalement, à l'aide des installations de vos applications—.

Dans cette mesure, supposons que le MyStatustableau contient les données suivantes - avec des valeurs PK qui sont (a) compatibles avec l'utilisateur final, le programmeur d'application et DBA et (b) petites et rapides en termes d'octets au niveau de l' implémentation physique -:

 + -——————————- + -—————————- +
 | StatusCode | Nom       |
 + -——————————- + -—————————- +
 | R | Demandé |
 + ------------ + ----------- +
 | A | Accepté |
 + ------------ + ----------- +
 | D | Refusée |
 + ------------ + ----------- +
 | B | Bloqued |
 + ------------ + ----------- +

Ainsi, le FriendshipStatustableau peut contenir des données comme indiqué ci-dessous:

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

Comme vous pouvez le voir, on peut dire que le FriendshipStatustableau sert à constituer une série chronologique .


Postes pertinents

Vous pourriez tout aussi bien être intéressé par:

  • Cette réponse dans laquelle je suggère une méthode de base pour traiter une relation plusieurs-à-plusieurs commune entre deux types d'entités dissemblables.
  • Le diagramme IDEF1X illustré à la figure 1 qui illustre cette autre réponse . Portez une attention particulière aux types d'entités nommés Mariage et Descendance , car ce sont deux autres exemples de la façon de gérer le «problème d'explosion des pièces».
  • Ce message qui présente une brève délibération sur la conservation de différentes informations dans une seule colonne.
MDCCL
la source