Дизайн базы данных: нормализация отношения «(многие ко многим) ко многим»

13

Укороченная версия

Мне нужно добавить фиксированное количество дополнительных свойств для каждой пары в существующем соединении «многие ко многим». Переходя к диаграммам ниже, какой из вариантов 1-4 является наилучшим способом с точки зрения преимуществ и недостатков, чтобы достичь этого путем расширения базового варианта? Или есть лучшая альтернатива, которую я здесь не рассматривал?

Более длинная версия

В настоящее время у меня есть две таблицы в отношении многие ко многим через промежуточную таблицу соединений. Теперь мне нужно добавить дополнительные ссылки на свойства, которые принадлежат к паре существующих объектов. У меня есть фиксированное количество этих свойств для каждой пары, хотя одна запись в таблице свойств может применяться к нескольким парам (или даже использоваться несколько раз для одной пары). Я пытаюсь определить лучший способ сделать это, и мне трудно разобраться, как думать о ситуации. С семантической точки зрения кажется, что я могу описать это как любое из следующего с одинаковой точностью:

  1. Одна пара связана с одним набором фиксированного числа дополнительных свойств
  2. Одна пара связана со многими дополнительными свойствами
  3. Многие (два) объекта связаны с одним набором свойств
  4. Многие объекты связаны со многими свойствами

пример

У меня есть два типа объекта, х и у, каждый с уникальными идентификаторами, и связывающую таблицу 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 (3б)

В основном так же, как вариант 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 здесь

Джек говорит, попробуйте topanswers.xyz
источник
-3

Я полагаю, что любой из этих вариантов может сработать, но я бы выбрал вариант 1, если число условий действительно установлено на 3, и вариант 2, если это не так. Бритва Оккама также работает для проектирования баз данных, при прочих равных условиях самый простой дизайн обычно является лучшим.

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

Мэтью Сонтум
источник