Створення структури бази даних дружби: чи слід використовувати багатозначний стовпець?


9

Скажімо, у мене є таблиця під назвою User_FriendList, яка має такі характеристики:

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

Припустимо, що у вказаній таблиці є такі дані:

 + ---- + --------- + --------------------------- +
 | Ідентифікатор | User_ID | Список друзів_ID             |
 + ---- + --------- + --------------------------- +
 | 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 |
 + ---- + --------- + --------------------------- +

Примітка: ":" (двокрапка) - це роздільник, коли вибухне в PHP в ан array.

Запитання

Тому:

  • Це зручний спосіб "зберігати" IDsтовари FriendList?

  • Або, натомість, чи маю я мати окремі рядки із лише одним FriendIdзначенням у кожному з них, і коли мені потрібно отримати всі рядки заданого списку , просто виконувати запит, як SELECT * FROM UserFriendList WHERE UserId = 1?


3
Я рекомендую вам прочитати: stackoverflow.com/questions/3653462 / ...
tombom

Це дещо зручний спосіб зберігання ідентифікаторів, якщо ви не плануєте з ними нічого робити і не особливо дбаєте про якість даних.
mustaccio

Я думаю, що codedodle.com/2014/12/social-network-friends-database.html може бути одним із найкращих рішень.
Гупта

Відповіді:


19

Управління окремою інформацією

Припустимо, що у вашому домені бізнесу

  • Користувач може мати нульові один або багато друг ;
  • Друг повинен першим бути зареєстрований в якості користувача ; і
  • ви будете шукати та / або додавати, та / або вилучати та / або змінювати поодинокі значення списку друзів ;

то кожна конкретна дата, зібрана у Friendlist_IDsбагатозначному стовпчику, являє собою окрему інформацію, яка має дуже точне значення. Тому зазначена колонка

  • тягне за собою належну групу явних обмежень та
  • його значення можуть піддаватися маніпулюванню окремо за допомогою декількох реляційних операцій (або їх комбінацій).

Коротка відповідь

Отже, вам слід зберегти кожне зі Friendlist_IDsзначень у (а) стовпці, який приймає виключно одне єдине значення на рядок у (b) таблиці, яка представляє тип асоціації на концептуальному рівні, який може відбуватися між Користувачами , тобто Дружба - як Я прикладу приклади в наступних розділах.

Таким чином, ви зможете обробити (i) зазначену таблицю як математичне відношення, і (ii) вказаний стовпець як атрибут математичного відношення - настільки ж, як дозволяє MySQL та його діалект SQL, звичайно -.

Чому?

Оскільки реляційна модель даних , створена доктором Е. Ф. Коддом , вимагає мати таблиці, що складаються з стовпців, що містять рівно одне значення відповідного домену чи типу в рядку; отже, оголошення таблиці зі стовпцем, яка може містити більше одного значення домену чи типу, про які йдеться (1), не представляє математичного відношення і (2) не дозволить отримати переваги, запропоновані у вищезгаданій теоретичній базі.

Моделювання дружби між користувачами : Спочатку визначте правила ділового середовища

Я настійно рекомендую почати формувати базу даних, що обмежує - перш за все інше - відповідну концептуальну схему в силу визначення відповідних правил ведення бізнесу, які, серед інших факторів, повинні описувати типи взаємозв'язків, що існують між різними аспектами інтересу, тобто , типи юридичних осіб та їх властивості ; наприклад:

  • Користувач в першу чергу ідентифікується його UserId
  • Користувач черзі визначається комбінацією його або її FirstName , LastName , Пол і народження
  • Користувач черзі ідентифікується його Ім'я
  • Користувач є Запитом нульових один або-багато Дружби
  • Користувач є Адресат нульовий один або-багато Дружби
  • Дружба в першу чергу визначається комбінацією його RequesterId і його AddresseeId

Діаграма IDEF1X експозиторії

Таким чином, я зміг отримати схему IDEF1X 1, показану на рисунку 1 , яка інтегрує більшість правил, сформульованих раніше:

Малюнок 1. Діаграма IDEF1X Дружби користувачів

Як зображено, Заявник та Адресат - це позначення, які виражають ролі, які виконуються конкретними Користувачами, які беруть участь у певній дружбі .

Оскільки це так, то дружба тип сутності зображує вид Асоціації багато-до-багатьох (M: N) відношення потужності , що може включати в себе різні входжень в той же тип об'єкта, тобто користувача . Як такий, він є прикладом класичної конструкції, відомої як "Біблія матеріалів" або "Вибух частин".


1 Визначення інтеграції для інформаційного моделювання ( IDEF1X ) - це дуже рекомендована методика, яка була встановлена ​​як стандарт в грудні 1993 р. Національним інститутом стандартів і технологій США (NIST). Це ґрунтується на (a) ранньому теоретичному матеріалі, створеному єдиним автором реляційної моделі, тобто доктором Е.Ф. Коддом ; (b)перегляд даних про відносини між сутностями , розроблений доктором П.П. Ченом ; а також (c) техніку проектування логічної бази даних, створену Робертом Г. Брауном.


Ілюстративний логічний дизайн SQL-DDL

Тоді, з діаграми IDEF1X, представленої вище, декларування DDL-компоновки як такої, що випливає, є набагато "природнішою":

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

Таким чином:

  • кожна базова таблиця представляє індивідуальний тип сутності;
  • кожен стовпець означає єдину властивість відповідного типу сутності;
  • специфічний тип даних a фіксується для кожного стовпця , щоб гарантувати, що всі значення, які він містить, належать певному та чітко визначеному набору , будь то INT, DATETIME, CHAR тощо; і
  • кілька обмежень b конфігуруються (декларативно) для того, щоб твердження у вигляді рядків, що зберігаються у всіх таблицях, відповідали правилам бізнесу, визначеним на концептуальній схемі.

Переваги однозначної колонки

Як показано, ви можете, наприклад:

  • Скористайтесь референтною цілісністю, що застосовується системою управління базами даних (СУБД для стислості) для Friendship.AddresseeIdстовпця, оскільки обмеження її як ІНОЗЕМНИЙ КЛЮЧ (FK для стислості), що робить посилання на UserProfile.UserIdстовпець, гарантує, що кожне значення вказує на існуючий рядок.

  • Створіть складений ОСНОВНИЙ КЛЮЧ (ПК), складений із комбінації стовпців (Friendship.RequesterId, Friendship.AddresseeId), що допомагає елегантно розрізнити всі ВСТАНОВІ ряди та, природно, захистити їх унікальність .

    Звичайно, це означає, що додавання додаткового стовпчика для сурогатних значень, призначених системою (наприклад, одного, встановленого з властивістю IDENTITY в Microsoft SQL Server або з атрибутом AUTO_INCREMENT в MySQL) та допоміжного INDEX є абсолютно зайвим .

  • Обмежте збережені значення у Friendship.AddresseeIdточному типі даних c (який повинен відповідати, наприклад, встановленому для UserProfile.UserId, у цьому випадку INT), дозволяючи СУБД дбати про відповідну автоматичну перевірку.

    Цей фактор може також допомогти (a) використовувати відповідні функції вбудованого типу та (b) оптимізувати використання дискового простору .

  • Оптимізуйте пошук даних на фізичному рівні, конфігурувавши невеликі та швидкі підпорядковані INDEXе для Friendship.AddresseeIdстовпця, оскільки ці фізичні елементи можуть істотно допомогти у прискоренні запитів, що стосуються зазначеного стовпця.

    Звичайно, ви можете, наприклад, поставити одноколонний INDEX для Friendship.AddresseeIdодного, багато стовпця, який охоплює Friendship.RequesterIdта Friendship.AddresseeIdабо обидва.

  • Уникайте зайвої складності, введеної "пошуком" відмінних значень, які збираються всередині одного і того ж стовпця (швидше за все, дублюється, неправильно набрано тощо), ходу дій, яка в кінцевому підсумку уповільнить функціонування вашої системи, тому що ви б доведеться вдатися до ресурсних і трудомістких нереляційних методів для виконання зазначеного завдання.

Отже, є кілька причин, які вимагають ретельного аналізу відповідного бізнес-середовища, щоб точно позначити тип d кожного стовпця таблиці.

Як пояснено, роль, яку відіграє конструктор баз даних, є першорядною для найкращого використання (1) переваг логічного рівня, пропонованих реляційною моделлю, та (2) фізичних механізмів, що надаються СБД на вибір.


a , b , c , d Очевидно, що під час роботи з платформами SQL (наприклад, Firebird та PostgreSQL ), які підтримують створення DOMAIN (відмітна реляційна особливість), ви можете оголошувати стовпці, які приймають лише ті значення, які належать до їх відповідних (що сильно обмежено і іноді спільно) DOMAIN.


Одна або кілька прикладних програм, що обмінюються розглянутою базою даних

Коли ви повинні використовувати arraysв коді прикладної програми (и) екранної базу даних, вам просто потрібно , щоб отримати відповідний набір даних (и) в повному обсязі , а потім «прив'язати» його (їх) до відносно структури коди або виконайте пов'язаний процес (и) додатків, який має відбутися.

Подальші переваги однозначних стовпців: Розширення структури бази даних набагато простіше

Ще одна перевага зберігання AddresseeIdточки даних у зарезервованому та належним чином введеному стовпчику полягає в тому, що це значно полегшує розширення структури бази даних, як я поясню нижче.

Прогресування сценарію: Включення концепції дружби

Оскільки дружба може розвиватися з часом, вам, можливо, доведеться відслідковувати таке явище, тому вам доведеться (i) розширити концептуальну схему та (ii) оголосити ще кілька таблиць у логічному макеті. Отже, давайте організуємо наступні правила ведення бізнесу для розмежування нових корпорацій:

  • Дружба займає один-ко-многим FriendshipStatuses
  • FriendshipStatus в першу чергу визначається комбінацією його RequesterId , його AddresseeId і його SpecifiedDateTime
  • A User визначає нуль-один-або-багато FriendshipStatuses
  • A Статус класифікує нуль-один-або-багато FriendshipStatuses
  • Статус головним чином ідентифікується своїм StatusCode
  • Статус черзі ідентифікується по його імені

Розширена діаграма IDEF1X

Послідовно попередня діаграма IDEF1X може бути розширена, щоб включити нові типи сутностей та типи взаємозв'язків, описані вище. Діаграма, що зображує попередні елементи, пов'язані з новими, представлена ​​на рисунку 2 :

Малюнок 2. Діаграма IDEF1X про статус дружби

Доповнення логічної структури

Після цього ми можемо подовжити макет DDL за допомогою таких декларацій:

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

Отже, кожного разу, коли статус даної Дружби потрібно оновлювати, Користувачам слід буде ВСТАВИТИ новий FriendshipStatusрядок, що містить:

  • відповідні RequesterIdта AddresseeIdзначення - взяті з відповідного Friendshipрядка;

  • нове і змістовне StatusCodeзначення - отримане від MyStatus.StatusCode-;

  • точний момент INSERTion, тобто, SpecifiedDateTimeпереважно, використовуючи функцію сервера, щоб ви могли отримати і зберегти її надійно; і

  • SpecifierIdзначення , яке буде вказувати відповідне , UserIdщо увійшло в новому FriendshipStatusв систему -ideally, за допомогою вашої програми (s) точки-.

Припустимо, що у MyStatusтаблицю містяться такі дані - із значеннями ПК, які (а) є кінцевим користувачем, програмістом додатків та зручним для DBA та (б) малим та швидким у перерахунку на байти на рівні фізичної реалізації -:

 + -——————————— + +——————————— +
 | StatusCode | Назва       |
 + -——————————— + +——————————— +
 | R | Запитано |
 + ------------ + ----------- +
 | А | Прийнято |
 + ------------ + ----------- +
 | Д | Відхилено |
 + ------------ + ----------- +
 | Б | Заблоковано |
 + ------------ + ----------- +

Отже, FriendshipStatusтаблиця може містити дані, як показано нижче:

 + -———————————— + +———————————————————————————————————————— ———- + -——————————— + -———————————— +
 | RequesterId | АдресатId | SpecifiedDateTime        | StatusCode | SpecifierId |
 + -———————————— + +———————————————————————————————————————— ———- + -——————————— + -———————————— +
 | 1750 | 1748 | 2016-04-01 16: 58: 12.000 | R | 1750 |
 + ------------- + ------------- + --------------------- ---- + ------------ + ------------- +
 | 1750 | 1748 | 2016-04-02 09: 12: 05.000 | А | 1748 |
 + ------------- + ------------- + --------------------- ---- + ------------ + ------------- +
 | 1750 | 1748 | 2016-04-04 10: 57: 01.000 | Б | 1750 |
 + ------------- + ------------- + --------------------- ---- + ------------ + ------------- +
 | 1750 | 1748 | 2016-04-07 07: 33: 08.000 | R | 1748 |
 + ------------- + ------------- + --------------------- ---- + ------------ + ------------- +
 | 1750 | 1748 | 2016-04-08 12: 12: 09.000 | А | 1750 |
 + ------------- + ------------- + --------------------- ---- + ------------ + ------------- +

Як бачите, можна сказати, що FriendshipStatusтаблиця служить меті, що включає часовий ряд .


Відповідні повідомлення

Вас також можуть зацікавити:

  • Ця відповідь, в якій я пропоную основний метод для вирішення загальної взаємозв'язку між багатьма різними типами сутності.
  • Діаграма IDEF1X, показана на рисунку 1, що ілюструє цю іншу відповідь . Зверніть особливу увагу на типи сутностей під назвою " Шлюб та потомство" , оскільки вони є ще двома прикладами, як вирішити "Проблему вибуху частин".
  • Ця публікація, в якій представлено коротке обговорення щодо зберігання різних відомостей у межах однієї колонки.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.