Дизайн бази даних: нормалізація відносин "(багато-багато-багато) -до-багатьох"


14

Коротка версія

Я маю додати фіксовану кількість додаткових властивостей до кожної пари в існуючому об'єднанні багато-до-багатьох. Переходячи до діаграм нижче, який із варіантів 1-4 є найкращим способом, з точки зору переваг та недоліків, досягти цього шляхом розширення базової справи? Або є краща альтернатива, яку я тут не розглядав?

Більш довга версія

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

  1. Одна пара пов'язана з одним набором фіксованої кількості додаткових властивостей
  2. Одна пара пов'язана з багатьма додатковими властивостями
  3. Багато (два) об'єкти, пов'язані з одним набором властивостей
  4. Багато об’єктів пов'язані з багатьма властивостями

Приклад

У мене є два типи об'єктів, X і Y, кожен з унікальними ідентифікаторами, і таблиця зв’язку objx_objyзі стовпцями x_idі y_id, які разом утворюють первинний ключ для посилання. Кожен X може бути пов'язаний з багатьма Ys, і навпаки. Це налаштування для моїх існуючих стосунків «багато до багатьох».

Базова справа

Базовий корпус

Тепер додатково у мене є набір властивостей, визначених в іншій таблиці, і набір умов, за яких дана пара (X, Y) повинна мати властивість P. Кількість умов є фіксованим і однаковим для всіх пар. Вони в основному кажуть "У ситуації C1 пара (X1, Y1) має властивість P1", "У ситуації C2 пара (X1, Y1) має властивість P2" і так далі, для трьох ситуацій / умов для кожної пари в об'єднанні стіл.

Варіант 1

У моїй нинішній ситуації є рівно три такі умови, і у мене немає ніяких підстав очікувати , що для збільшення, тому одна можливість полягає в тому, щоб додати стовпці c1_p_id, c2_p_idі c3_p_idдо featx_featy, вказавши для даного x_idі y_id, що властивість p_idдля використання в кожному з трьох випадків .

Варіант 1

Мені це не здається чудовою ідеєю, оскільки це ускладнює SQL для вибору всіх властивостей, застосованих до функції, і не може масштабуватись до інших умов. Однак він виконує вимогу певної кількості умов для пари (X, Y). Насправді це єдиний варіант, який робить це.

Варіант 2

Створіть таблицю умов condта додайте ідентифікатор умови до первинного ключа таблиці з'єднання.

Варіант 2

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

SELECT objx.*, objy.* FROM objx
  INNER JOIN objx_objy ON objx_objy.x_id = objx.id
  INNER JOIN objy ON objy.id = objx_objy.y_id

Тоді я повинен додати DISTINCTпункт, щоб уникнути повторюваних записів. Здається, це втратило той факт, що кожна пара повинна існувати лише один раз.

Варіант 3

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

Варіант 3

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

Варіант 4 (3b)

В основному те саме, що варіант 3, але без створення додаткового поля ідентифікатора. Це досягається шляхом розміщення обох оригінальних ідентифікаторів у новій таблиці приєднання, тому вона містить x_idі y_idполя, а не xy_id.

Варіант 4

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

Підсумок

Моє відчуття, що варіанти 3 і 4 досить схожі, щоб я міг перейти з будь-яким. Я, мабуть, мав би до цього часу, якби не вимога невеликої, фіксованої кількості посилань на властивості, що робить варіант 1 здається більш розумним, ніж це було б інакше. На основі дуже обмеженого тестування додавання DISTINCTпункту до моїх запитів, схоже, не впливає на ефективність у цій ситуації, але я не впевнений, що варіант 2 представляє ситуацію, як і інші, через притаманне дублювання, спричинене розміщенням однакові (X, Y) пари в декількох рядках таблиці зв’язків.

Чи є один із цих варіантів найкращим моїм шляху чи є інша структура, яку я повинен розглянути?


Загалом, 1 і 4 здаються найкращими варіантами, я згоден. Нелегко застосувати фіксовану (3) кількість властивостей за допомогою варіанту 4, але я думаю, що це можливо.
ypercubeᵀᴹ

Для DISTINCTпункту, я думав про такий запит, як той, що знаходиться в кінці №2, який посилається xі yнаскрізь, xycале не посилається на c... Отже, якщо я (x_id, y_id, c_id)обмежився UNIQUEрядками (1,1,1)і (1,1,2), значить SELECT x.id, y.id FROM x JOIN xyc JOIN y, я поверну два однакові рядки (1,1), і (1,1).
Майкл Андервуд

1
Добренько. Я б у будь-якому разі відхилив варіант 2. Я б пішов або з 1, або з 4.
ypercubeᵀᴹ

Чим більше я думаю про це, тим більше відчуваю, що обмеження кількості властивостей рівно трьома є найменш важливим з моїх вимог. Тож забороняючи додаткові конструктивні відгуки протягом наступного часу, я, мабуть, піду з №4 на цьому етапі. Дякуємо за ваш внесок, @ypercube!
Майкл Андервуд

Відповіді:


7
  • Варіант 1

    * Це не здається мені чудовою ідеєю, оскільки це ускладнює SQL для вибору всіх властивостей, застосованих до функції ...

    Це не обов'язково ускладнює запит SQL (див. Висновок нижче).

    ... і не може масштабуватись до інших умов ...

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

    Однак він виконує вимогу певної кількості умов для пари (X, Y). Насправді це єдиний варіант, який робить це. *

    Так, і хоча ви кажете в коментарі, що це "найменш важлива моя вимога", ви не сказали, що це зовсім не має значення.

  • Варіант 2

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

    Я думаю, що ви можете відхилити цей варіант через згадувані вами ускладнення. objx_objyТаблиця, ймовірно, буде рушійною стіл для деяких з ваших запитів (наприклад , «вибрати всі властивості , застосовані до функції», які я приймаю на увазі всі властивості , застосовані до objxабо objy). Ви можете використовувати перегляд для попереднього застосування, DISTINCTтому справа не в тому, щоб ускладнювати запити, але це буде масштабувати дуже погану ефективність за дуже невеликий прибуток.

  • Варіант 3

    Чи має сенс створити новий ідентифікатор, який не визначає нічого, крім існуючих ідентифікаторів?

    Ні, це не так - Варіант 4 кращий у всіх відношеннях.

  • Варіант 4

    … Він в основному дублює цілу таблицю кілька разів (або відчувається так, все одно), тому також не здається ідеальним.

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

Висновок

Моїм уподобанням буде варіант 1, якщо кількість властивостей на одного objx_objy, ймовірно, буде стабільним, і якщо ви не можете уявити, щоб коли-небудь додати більше, ніж кілька зайвих. Це також єдиний варіант, який примушує обмеження "кількість властивостей = 3" - примусове застосування подібного обмеження у варіанті 4, ймовірно, передбачає додавання c1_p_id… стовпців до таблиці xy у будь-якому випадку *.

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

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

Таблиця варіанту 1:

create table prop(id integer primary key);
create table objx(id integer primary key);
create table objy(id integer primary key);

create table objx_objy(
  x_id integer references objx
, y_id integer references objy
, c1_p_id integer not null references prop
, c2_p_id integer not null references prop
, c3_p_id integer not null references prop
, primary key (x_id, y_id)
);

insert into prop(id) select generate_series(90,99);
insert into objx(id) select generate_series(10,12);
insert into objy(id) select generate_series(20,22);

insert into objx_objy(x_id,y_id,c1_p_id,c2_p_id,c3_p_id)
select objx.id, objy.id, 90, 91, 90+floor(random()*10)
from objx cross join objy;

перегляньте варіант "емуляції" 4:

create view objx_objy_prop as
select x_id
     , y_id
     , unnest(array[1,2,3]) c_id
     , unnest(array[c1_p_id,c2_p_id,c3_p_id]) p_id
from objx_objy;

"вибрати всі властивості, застосовані до функції":

select distinct p_id from objx_objy_prop where x_id=10 order by p_id;

/*
|p_id|
|---:|
|  90|
|  91|
|  97|
|  98|
*/

dbfiddle тут


-3

Я вважаю, що будь-який із цих варіантів може спрацювати, але я б пішов з варіантом 1, якщо кількість умов справді встановлено на рівні 3, і з варіантом 2, якщо його немає. Бритва Оккама також працює для проектування баз даних, але всі інші фактори, прирівнені до найпростішої конструкції, як правило, найкращі.

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

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