Projektowanie struktury bazy danych przyjaźni: Czy powinienem używać kolumny wielowartościowej?

9

Powiedzmy, że mam tabelę o nazwie User_FriendList, która ma następujące cechy:

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

Przypuśćmy, że wspomniana tabela zawiera następujące dane:

 + ---- + --------- + --------------------------- +
 | ID | ID_użytkownika | 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 |
 + ---- + --------- + --------------------------- +

Uwaga: „:” (dwukropek) jest ogranicznikiem podczas eksplozji w PHP na an array.

pytania

Więc:

  • Jest to wygodny sposób do „sklepu” IDso tematyce FriendList?

  • Czy zamiast tego powinienem mieć pojedyncze wiersze z tylko jedną pojedynczą FriendIdwartością w każdym z nich, a kiedy muszę pobrać wszystkie wiersze z danej listy , po prostu wykonaj zapytanie podobne SELECT * FROM UserFriendList WHERE UserId = 1?

yeln
źródło
3
Polecam przeczytanie tego: stackoverflow.com/questions/3653462/…
tombom 20.04.2016
Jest to dość wygodny sposób na przechowywanie identyfikatorów, o ile nie planujesz nic z nimi zrobić i nie dbasz o jakość danych.
mustaccio
Myślę, że codedodle.com/2014/12/social-network-friends-database.html może być jednym z najlepszych rozwiązań.
Gupta

Odpowiedzi:

19

Zarządzanie pojedynczą informacją

Zakładając, że w Twojej domenie biznesowej

  • Użytkownik może mieć zero-jeden-lub-wielu przyjaciół ;
  • Friend musi najpierw zostać zarejestrowany jako użytkownik ; i
  • będziesz wyszukiwał i / lub dodawał i / lub usuwał i / lub modyfikował pojedyncze wartości Listy znajomych ;

następnie każdy konkretny punkt odniesienia zebrany w Friendlist_IDskolumnie wielowartościowej reprezentuje oddzielną informację, która ma bardzo dokładne znaczenie. Dlatego wspomniana kolumna

  • pociąga za sobą odpowiednią grupę wyraźnych ograniczeń oraz
  • jego wartości mogą być manipulowane indywidualnie za pomocą kilku operacji relacyjnych (lub ich kombinacji).

Krótka odpowiedź

W związku z tym powinieneś zachować każdą z Friendlist_IDswartości w (a) kolumnie, która akceptuje tylko jedną wyłączną wartość na wiersz w (b) tabeli, która reprezentuje typ powiązania na poziomie koncepcyjnym, który może mieć miejsce między użytkownikami , tj. Przyjaźń - jako Zilustruję to następującymi sekcjami—.

W ten sposób będziesz w stanie obsłużyć (i) wspomnianą tabelę jako relację matematyczną oraz (ii) wspomnianą kolumnę jako atrybut relacji matematycznej - podobnie jak MySQL i oczywiście jego dialekt SQL - oczywiście.

Dlaczego?

Ponieważ relacyjny model danych , stworzony przez dr E. F. Codda , wymaga posiadania tabel składających się z kolumn zawierających dokładnie jedną wartość odpowiedniej domeny lub typu na wiersz; dlatego zadeklarowanie tabeli z kolumną, która może zawierać więcej niż jedną wartość domeny lub typu, o którym mowa (1), nie reprezentuje relacji matematycznej i (2) nie pozwoliłoby na uzyskanie korzyści zaproponowanych w wyżej wymienionych ramach teoretycznych.

Modelowanie przyjaźni między użytkownikami : Najpierw należy zdefiniować reguły środowiska biznesowego

Zdecydowanie zalecam rozpoczęcie tworzenia bazy danych określającej - przed czymkolwiek innym - odpowiedni schemat pojęciowy na podstawie definicji odpowiednich reguł biznesowych, które między innymi muszą opisywać rodzaje wzajemnych powiązań między różnymi aspektami zainteresowania, tj. , odpowiednie typy jednostek i ich właściwości ; na przykład:

  • Użytkownik jest identyfikowany głównie przez jego UserId
  • Użytkownik jest naprzemiennie zidentyfikowane przez połączenie jego FirstName , LastName , płci i urodzenia
  • Użytkownik jest naprzemiennie zidentyfikowane przez jego Nazwa użytkownika
  • Użytkownik jest Requestera zero-jeden-or-wieloma przyjaźniami
  • Użytkownik jest Adresat zero-jeden-or-wieloma przyjaźniami
  • Przyjaźń jest identyfikowana głównie przez połączenie jej RequesterId i jego AddresseeId

Schemat IDEF1X dla ekspozytora

W ten sposób udało mi się uzyskać diagram IDEF1X 1 pokazany na rysunku 1 , który integruje większość wcześniej sformułowanych reguł:

Rysunek 1. Diagram przyjaźni użytkownika IDEF1X

Jak pokazano, Wnioskodawca i Adresat są oznaczeniami, które wyrażają Role realizowane przez konkretnych Użytkowników biorących udział w danej Przyjaźni .

W związku z tym typ encji Przyjaźń przedstawia typ asocjacji o stosunku liczności wiele do wielu (M: N), który może obejmować różne przypadki tego samego typu encji, tj . Użytkownika . Jako taki, jest to przykład klasycznej konstrukcji znanej jako „Zestawienie materiałów” lub „Wybuch części”.


1 Integration Definition for Information Modeling ( IDEF1X ) to wysoce godna polecenia technika, która została ustanowiona jako standard w grudniu 1993 r. Przez amerykański Narodowy Instytut Norm i Technologii (NIST). Jest solidnie oparty na (a) wczesnym materiale teoretycznym autorstwa jedynego twórcy modelu relacyjnego, tj. Dr EF Codda ; na (b)widok danych relacji jednostka , opracowany przez dr PP Chen ; a także w (c) Logical Database Design Technique, stworzonej przez Roberta G. Browna.


Ilustracyjny logiczny projekt SQL-DDL

Następnie, zgodnie ze schematem IDEF1X przedstawionym powyżej, zadeklarowanie układu DDL, takiego jak poniższy, jest znacznie bardziej „naturalne”:

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

W ten sposób:

  • każda tabela podstawowa reprezentuje indywidualny typ jednostki;
  • każda kolumna oznacza wyłączną właściwość odpowiedniego typu jednostki;
  • określony typ danych a jest ustalony dla każdej kolumny , aby zagwarantować, że wszystkie zawarte w nim wartości należą do określonego i dobrze zdefiniowanego zestawu , czy to INT, DATETIME, CHAR itp .; i
  • wiele ograniczeń b jest konfigurowanych (deklaratywnie) w celu zapewnienia, że ​​twierdzenia w postaci wierszy zachowane we wszystkich tabelach spełniają reguły biznesowe określone na schemacie koncepcyjnym.

Zalety kolumny o pojedynczej wartości

Jak wykazano, możesz np .:

  • Skorzystaj z integralności referencyjnej wymuszonej przez system zarządzania bazą danych (DBMS dla zwięzłości) dla Friendship.AddresseeIdkolumny, ponieważ ograniczenie jej jako KLUCZA OBCEGO (FK dla zwięzłości), który odnosi się do UserProfile.UserIdkolumny, gwarantuje, że każda wartość wskazuje na istniejący wiersz.

  • Utwórz złożony KLUCZ PODSTAWOWY (PK) złożony z kombinacji kolumn (Friendship.RequesterId, Friendship.AddresseeId), pomagając elegancko odróżnić wszystkie WSTAWIONE rzędy i, naturalnie, chronić ich wyjątkowość .

    Oczywiście oznacza to, że dołączenie dodatkowej kolumny dla przypisanych przez system wartości zastępczych (np. Skonfigurowanej z właściwością IDENTITY w Microsoft SQL Server lub z atrybutem AUTO_INCREMENT w MySQL) i INDEKS pomocniczy jest całkowicie zbędny .

  • Ogranicz zachowane wartości Friendship.AddresseeIddo dokładnego typu danych c (który powinien pasować np. Do tego ustalonego dla UserProfile.UserId, w tym przypadku INT), pozwalając DBMS zająć się odpowiednią automatyczną weryfikacją.

    Ten czynnik może również pomóc (a) wykorzystać odpowiednie wbudowane funkcje typu i (b) zoptymalizować wykorzystanie miejsca na dysku .

  • Zoptymalizuj pobieranie danych na poziomie fizycznym, konfigurując małe i szybkie INDEKSY podrzędne dla Friendship.AddresseeIdkolumny, ponieważ te fizyczne elementy mogą znacznie pomóc w przyspieszeniu zapytań dotyczących tej kolumny.

    Oczywiście, możesz np. Ustawić INDEKS jednokolumnowy dla Friendship.AddresseeIdsamego, wielokolumnowy, który obejmuje Friendship.RequesterIdi Friendship.AddresseeId, lub jedno i drugie.

  • Unikaj niepotrzebnej złożoności wprowadzonej przez „wyszukiwanie” odrębnych wartości, które są gromadzone razem w tej samej kolumnie (najprawdopodobniej powielone, źle wpisane itp.), Co może spowolnić działanie systemu, ponieważ muszą skorzystać z czasochłonnych zasobów i metod nierelacyjnych, aby wykonać to zadanie.

Istnieje zatem wiele powodów, które wymagają dokładnej analizy odpowiedniego środowiska biznesowego w celu dokładnego zaznaczenia typu d każdej kolumny tabeli.

Jak wyjaśniono, rola projektanta bazy danych jest najważniejsza, aby jak najlepiej wykorzystać (1) korzyści na poziomie logicznym oferowane przez model relacyjny oraz (2) mechanizmy fizyczne zapewniane przez wybrany DBMS.


, b , c , d Widocznie podczas pracy z platformami SQL (np Firebird i PostgreSQL ), że tworzenie wsparcie domeny (charakterystyczny relacyjny funkcję), można zadeklarować kolumn tylko przyjąć wartości, które należą do ich prawnych (słusznie ograniczone, a czasem udostępnione) DOMAIN.


Jeden lub więcej aplikacji współdzielących rozważaną bazę danych

Gdy musisz zastosować arrayskod aplikacji, które uzyskują dostęp do bazy danych, musisz po prostu pobrać odpowiednie zestawy danych w całości, a następnie „powiązać” je z odpowiednią strukturą kodu lub wykonać powiązane procesy, które powinny mieć miejsce.

Dalsze zalety kolumn o jednej wartości: Rozszerzenia struktury bazy danych są znacznie łatwiejsze

Kolejną zaletą trzymania AddresseeIdpunktu danych w zarezerwowanej i odpowiednio wpisanej kolumnie jest to, że znacznie ułatwia rozszerzenie struktury bazy danych, co zilustruję poniżej.

Postęp scenariusza: włączenie koncepcji statusu przyjaźni

Ponieważ przyjaźnie mogą ewoluować w czasie, być może będziesz musiał śledzić takie zjawisko, więc będziesz musiał (i) rozwinąć schemat koncepcyjny i (ii) zadeklarować kilka dodatkowych tabel w układzie logicznym. A zatem, ustalmy kolejne reguły biznesowe, aby nakreślić nowe włączenia:

  • Przyjaźń posiada jeden-do-wielu FriendshipStatuses
  • Status FriendshipStatus jest przede wszystkim identyfikowany przez kombinację jego RequesterId , AddresseeId i SpecifiedDateTime
  • A Użytkownika określa zero-jeden-or-wiele FriendshipStatuses
  • A Stan klasyfikuje zero-jeden-or-wiele FriendshipStatuses
  • Stan jest identyfikowana głównie przez jego StatusCode
  • Stan jest naprzemiennie zidentyfikowane przez jego nazwy

Rozszerzony schemat IDEF1X

Sukcesywnie poprzedni schemat IDEF1X można rozszerzyć, aby uwzględnić nowe typy jednostek i typy powiązań opisane powyżej. Schemat przedstawiający poprzednie elementy związane z nowymi przedstawiono na rysunku 2 :

Rysunek 2. Diagram statusu przyjaźni IDEF1X

Uzupełnienia struktury logicznej

Następnie możemy wydłużyć układ DDL o następujące deklaracje:

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

W związku z tym za każdym razem, gdy status danej przyjaźni musi być aktualizowany, użytkownicy będą musieli WSTAWIĆ tylko nowy FriendshipStatuswiersz zawierający:

  • odpowiednie RequesterIdi AddresseeIdwartości - wzięte z odpowiedniego wiersza - Friendship;

  • nowa i znacząca StatusCodewartość - zaczerpnięta z MyStatus.StatusCode-;

  • dokładna chwila WSTAWIANIA, tj. - SpecifiedDateTimenajlepiej przy użyciu funkcji serwera, aby można było ją pobrać i zachować w niezawodny sposób -; i

  • SpecifierIdwartość, która wskazuje odpowiedni UserIdktóra weszła nowa FriendshipStatusw systemie -ideally, przy pomocy aplikacji (s) facilities-.

W tym zakresie załóżmy, że MyStatustabela zawiera następujące dane - z wartościami PK, które są (a) przyjazne dla użytkownika końcowego, programisty aplikacji i DBA oraz (b) małe i szybkie pod względem bajtów na poziomie implementacji fizycznej -:

 + -——————————- + -—————————- +
 | StatusCode | Imię       |
 + -——————————- + -—————————- +
 | R | Prośba |
 + ------------ + ----------- +
 | A | Zaakceptowano |
 + ------------ + ----------- +
 | D | Odrzucony |
 + ------------ + ----------- +
 | B | Bloqued |
 + ------------ + ----------- +

Tak więc FriendshipStatustabela może zawierać dane, jak pokazano poniżej:

 + -———————————- + -———————————- + -——————————————————— ———- + -——————————- + -———————————- +
 | RequesterId | Adresat | SpecifiedDateTime        | StatusCode | SpecifierId |
 + -———————————- + -———————————- + -——————————————————— ———- + -——————————- + -———————————- +
 | 1750 | 1748 | 01.04.2016, 16: 58: 12.000 | R | 1750 |
 + ------------- + ------------- + --------------------- ---- + ------------ + ------------- +
 | 1750 | 1748 | 02.04.2016 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 | 2016-04-08 12: 12: 09.000 | A | 1750 |
 + ------------- + ------------- + --------------------- ---- + ------------ + ------------- +

Jak widać, można powiedzieć, że FriendshipStatustabela służy szeregowi czasowemu .


Odpowiednie posty

Równie interesujące mogą być:

  • Ta odpowiedź, w której sugeruję podstawową metodę radzenia sobie ze wspólną relacją wiele do wielu między dwoma różnymi typami bytów.
  • Schemat IDEF1X pokazany na rycinie 1 ilustrujący tę drugą odpowiedź . Zwróć szczególną uwagę na typy jednostek o nazwie Małżeństwo i potomstwo , ponieważ są to dwa kolejne przykłady radzenia sobie z „Problemem eksplozji części”.
  • Ten post przedstawia krótkie rozważania na temat przechowywania różnych informacji w jednej kolumnie.
MDCCL
źródło