Як ви ефективно моделюєте спадкування в базі даних?


131

Які найкращі практики моделювання спадкування в базах даних?

Що таке компроміси (наприклад, допитливість)?

(Мене найбільше цікавлять SQL Server і .NET, але я також хочу зрозуміти, як інші платформи вирішують цю проблему.)


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


6
@PerformanceDBA Отже, що ви пропонуєте уникати спадкування в моделі БД? Скажімо, у нас є 50 різних типів викладачів, і ми хочемо пов’язати саме цього вчителя з класом. Як би ви цього домоглися, не маючи спадщини?
svlada

1
@svlada. Це реально реалізувати в RDb, тому потрібне "успадкування". Задайте питання, включіть таблиці defns та приклад, і я детально відповім на нього. Якщо ви зробите це в ОО термінах, це буде королівський безлад.
PerformanceDBA

Відповіді:


162

Існує кілька способів моделювання спадкування в базі даних. Який ви обираєте, залежить від ваших потреб. Ось кілька варіантів:

Таблиця на тип (TPT)

У кожного класу є своя таблиця. Базовий клас містить усі елементи базового класу в ньому, і кожен клас, який виходить з нього, має свою власну таблицю, з первинним ключем, який також є іноземним ключем до таблиці базового класу; Клас похідної таблиці містить лише різні елементи.

Так, наприклад:

class Person {
    public int ID;
    public string FirstName;
    public string LastName;
}

class Employee : Person {
    public DateTime StartDate;
}

Це призведе до таких таблиць, як:

table Person
------------
int id (PK)
string firstname
string lastname

table Employee
--------------
int id (PK, FK)
datetime startdate

Таблиця за перієрархією (TPH)

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

З огляду на класи вище, ви закінчите цю таблицю:

table Person
------------
int id (PK)
int rowtype (0 = "Person", 1 = "Employee")
string firstname
string lastname
datetime startdate

Для будь-яких рядків, що мають рядок 0 (Person), початковий термін завжди буде нульовим.

Стіл на бетон (TPC)

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

З огляду на класи вище, ви закінчуєте ці таблиці:

table Person
------------
int id (PK)
string firstname
string lastname

table Employee
--------------
int id (PK)
string firstname
string lastname
datetime startdate

23
"Що ви обираєте, залежить від ваших потреб", - уточнюйте, будь ласка, оскільки я думаю, що причини вибору становлять суть питання.
Олексій

12
Дивіться мій коментар до питання. Використання нових іменних імен для технічних термінів Rdb призводить до плутанини. "TPT" є супертипом-підтипом. "TPH" ненормалізований, груба помилка. "TPH" ще менше Нормований, ще одна груба помилка.
PerformanceDBA

45
Тільки DBA припускає, що денормалізація - це завжди помилка. :)
Бред Вілсон

7
Хоча я визнаю, що денормалізація призводить до підвищення продуктивності в деяких випадках, це цілком обумовлено неповним (або неіснуючим) розділенням між логічною та фізичною структурою даних у СУБД. На жаль, більшість комерційних СУБД страждають від цієї проблеми. @PerformanceDBA вірно. Недостатня нормалізація - це помилка судження, жертвуючи послідовністю даних для швидкості. На жаль, це вибір, який DBA або розробник ніколи не потребували б, якщо СУБД були розроблені належним чином. Для запису я не DBA.
Кеннет Кокран

6
@Brad Wilson Лише розробник денормалізує, "для продуктивності" чи іншим чином. Часто це не є нормалізацією, правда, це ненормалізовано. Те, що денормалізація чи ненормалізованість є помилкою, є фактом, підтримуваним теорією та переживаним мільйонами, це не є «презумпцією».
PerformanceDBA

133

Правильне проектування бази даних - це не що інше, як правильне проектування об'єктів.

Якщо ви плануєте використовувати базу даних для чогось іншого, ніж просто серіалізувати свої об'єкти (наприклад, звіти, запити, використання кількох додатків, бізнес-аналітики тощо), я не рекомендую будь-яке просте відображення від об’єктів до таблиць.

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

З огляду на це розуміння, ви бачите, що один тип в об'єктно-орієнтованій програмі може зберігатися через десяток різних відносин. І різноманітність типів (об'єднаних за спадщиною, асоціацією, об'єднанням або зовсім неприєднаними) можуть частково зберігатися в одному відношенні.

Найкраще запитати себе, які факти ви хочете зберігати, на які запитання ви хочете відповісти, які звіти ви хочете створити.

Після того, як буде створено належний дизайн БД, тоді це просто питання створення запитів / представлень, які дозволять вам серіалізувати об'єкти до цих відносин.

Приклад:

У системі бронювання готелів може знадобитися зберігання факту, що у Jane Doe є бронювання номера в Seaview Inn 10-12 квітня. Це атрибут суб’єкта замовника? Це атрибут суб’єкта готелю? Це об'єкт бронювання з властивостями, які включають клієнтів та готелі? Це може бути будь-яка або всі ці речі в об'єктно-орієнтованій системі. У базі даних це нічого такого. Це просто голий факт.

Щоб побачити різницю, розгляньте наступні два запити. (1) Скільки бронювання готелів у Джейн До на наступний рік? (2) Скільки номерів заброньовано на 10 квітня в готелі Seaview Inn?

В об'єктно-орієнтованій системі запит (1) є атрибутом сутності замовника, а запит (2) - атрибутом готельного об'єкта. Це об'єкти, які могли б викрити ці властивості у своїх API. (Хоча, очевидно, внутрішні механізми, за допомогою яких ці значення одержують, можуть містити посилання на інші об'єкти.)

У реляційній системі баз даних обидва запити вивчали беркаційне відношення, щоб отримати їх номери, і концептуально немає необхідності турбуватися з будь-яким іншим "об'єктом".

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


12
Нарешті, острів справжніх знань у морі невігластва (і відмови дізнаватися що-небудь за межами їхніх завдань). Погоджено, це не магія: якщо RDb розроблений з використанням принципів RDb, то "карта" або "проектування" будь-якого "класу" без особливих зусиль. Примушувати RDb до вимог на основі класу просто неправильно.
PerformanceDBA

2
Цікава відповідь. Як би ви запропонували моделювати приклад «Особи-працівник» у прийнятій відповіді?
сім форси

2
@ sevenforce-Дизайн БД дійсно залежить від вимог системи, які не задані. Немає майже достатньої кількості інформації, щоб прийняти рішення. У багатьох випадках щось подібне до дизайну "стіл на тип" може бути доречним, якщо не по-рабськи дотримуватися. Наприклад, дата початку, мабуть, є доброю властивістю для об'єкта працівника, але в базі даних це дійсно повинно бути поле в таблиці зайнятості, оскільки людину можна найняти кілька разів з кількома датами початку. Це не має значення для об'єктів (які б використовували найсвіжіші), але це важливо в базі даних.
Джефрі Л Уітлідж

2
Звичайно, моє запитання було головним чином щодо способу моделювання спадкування. Вибачте за недостатньо зрозуміле. Дякую. Як ви вже згадували, найімовірніше, має бути Employmentтаблиця, в якій зібрані всі робочі місця з їх датами початку. Тож якщо Employerважливо знати поточну дату початку зайнятості , то це може бути правильним випадком використання для особи View, яка включає це властивість шляхом запиту? (зауважте: здається, що через "-" відразу після мого
ніка

5
Це справжній дорогоцінний камінь відповіді. Нам знадобиться деякий час, щоб справді зануритися і вимагати певних вправ, щоб вийти правильно, але це вже вплинуло на мій процес розробки реляційних баз даних.
MarioDS

9

Коротка відповідь: ви цього не робите.

Якщо вам потрібно серіалізувати свої об'єкти, використовуйте ORM або ще краще щось на зразок активного запису чи поширеності.

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


3
+1 Спроба моделювати спадкування в базі даних - це марна трата хороших реляційних ресурсів.
Даніель Шпієк

7

Зразки TPT, TPH та TPC - це шляхи, якими ви йдете, як згадував Бред Вілсон. Але пара приміток:

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

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

Скажімо, ми хочемо створити базу даних, яка містить таку діаграму класових форм:

public class Shape {
int id;
Color color;
Thickness thickness;
//other fields
}

public class Rectangle : Shape {
Point topLeft;
Point bottomRight;
}

public class Circle : Shape {
Point center;
int radius;
}

Дизайн бази даних для вищевказаних класів може бути таким:

table Shape
-----------
int id; (PK)
int color;
int thichkness;
int rowType; (0 = Rectangle, 1 = Circle, 2 = ...)

table Rectangle
----------
int ShapeID; (FK on delete cascade)
int topLeftX;
int topLeftY;
int bottomRightX;
int bottomRightY;

table Circle
----------
int ShapeID; (FK on delete cascade)  
int centerX;
int center;
int radius;

4

Є два основні типи успадкування, які ви можете налаштувати в БД, таблицю за суттю та таблицю за Ієрархією.

Таблиця на сутність - це таблиця базової сутності, яка має спільні властивості всіх дочірніх класів. Тоді ви маєте для кожного дочірнього класу іншу таблицю, у якій лише властивості, застосовні до цього класу. Вони пов'язані 1: 1 своїми ПК

alt текст

Таблиця за ієрархією - це те, коли всі класи поділяють таблицю, а необов'язкові властивості є нульовими. Їх також є полем дискримінації, яке є числом, яке позначає тип, який в даний час містить запис

alt текст SessionTypeID є дискримінатором

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

Редагувати: зображення, які я показую тут, - це знімки екрана проекту, над яким я працюю. Зображення Asset не є повним, отже, порожнечею, але це було головним чином, щоб показати, як його налаштування, а не що розмістити всередині ваших таблиць. Це залежить від вас;). Таблиця сеансів містить інформацію про сеанси віртуальної співпраці та може бути декількох типів сеансів, залежно від типу співпраці.


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

Чи можете ви додати посилання, звідки походить ілюстрація?
chryss

Де є образи, про які ви говорите в кінці своєї відповіді?
Муса Гайдарі

1

Ви б нормалізували свою базу даних, і це насправді відобразило б ваше спадщину. Це може призвести до погіршення продуктивності, але саме так відбувається з нормалізацією. Ймовірно, вам доведеться використовувати здоровий глузд, щоб знайти баланс.


2
чому люди вважають, що нормалізація бази даних погіршує продуктивність? Люди також думають, що принцип DRY погіршує продуктивність коду? звідки береться це неправильне сприйняття?
Стівен А. Лоу

1
Можливо, тому що денормалізація може покращити продуктивність, отже, нормалізація знижує її, відносно кажучи. Не можу сказати, що я з цим згоден, але, мабуть, так і виникло.
Меттью Шарлі

2
На початку нормалізація може мати незначний вплив на продуктивність, але з часом, коли кількість рядків збільшується, ефективні JOIN почнуть перевершувати об'ємні таблиці. Звичайно, нормалізація має й інші, більші переваги - послідовність та відсутність надмірності тощо.
Роб

1

повторення подібної відповіді теми

У ІЛИ зіставлянні карти спадкування відображається у батьківській таблиці, де батьківські та дочірні таблиці використовують один ідентифікатор

наприклад

create table Object (
    Id int NOT NULL --primary key, auto-increment
    Name varchar(32)
)
create table SubObject (
    Id int NOT NULL  --primary key and also foreign key to Object
    Description varchar(32)
)

SubObject має зовнішній ключ до Object. коли ви створюєте рядок SubObject, спочатку слід створити рядок Object та використовувати Id в обох рядках

EDIT: якщо ви також шукаєте моделювати поведінку, вам знадобиться таблиця Type, яка перераховує відносини успадковування між таблицями та вказує назву складання та класу, які реалізували поведінку кожної таблиці

здається, надмірність, але все залежить від того, для чого ви хочете її використовувати!


Ця дискусія закінчилася тим, що додати кілька стовпців до кожної таблиці, а не про моделювання спадкування. Я думаю, що назву цієї дискусії слід змінити, щоб краще відобразити характер питання та дискусії.
Навіть Мієн

1

Використовуючи SQL ALchemy (Python ORM), ви можете зробити два типи успадкування.

Я мав досвід використання сингл-таблиці та розбірливого стовпчика. Наприклад, база даних Sheep (не жартуйте!) Зберігала всі вівці в одній таблиці, а Овен та Овець оброблялися за допомогою гендерного стовпця в цій таблиці.

Таким чином, ви можете запитувати всіх овець і отримувати всіх овець. Або ви можете запитувати лише Рама, і він отримає лише Рамс. Ви також можете робити такі речі, як стосунки, які можуть бути лише Барана (тобто, Сир Вівці) тощо.


1

Зауважимо, що деякі двигуни бази даних вже надають механізми успадковування на зразок Postgres . Подивіться на документацію .

Наприклад, ви б запитали систему персоналу / працівника, описану у відповіді вище, як це:

  / * Тут відображається ім’я всіх осіб або службовців * /
  ВИБІРТЕ ім’я від особи; 

  / * Тут відображається дата початку роботи лише всіх працівників * /
  ВИБІРАТИ початок від працівника;

Оскільки це вибір вашої бази даних, вам не потрібно бути особливо розумним!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.