Как наилучшим образом реализовать отношение 1: 1 в РСУБД? - PullRequest
2 голосов
/ 13 ноября 2009

Вчера, работая над проектом, я столкнулся со странными отношениями 1: 1, которые заставили меня задуматься - как лучше всего это реализовать (очевидно, мы сделали это неправильно: D)

Идея состоит в том, что есть два типа сущностей, A и B. Каждый из них может прекрасно существовать самостоятельно, но они также могут иметь связь между ними. Если есть ссылка, то она должна быть ссылкой 1: 1 и работать в обоих направлениях.

Это как бутылка и крышка. Они могут существовать отдельно, но при соединении вместе бутылка будет иметь только одну крышку, и крышка будет прикреплена только к одной (и той же) бутылке.

Как бы вы внедрили эти отношения, учитывая все лучшие практики нормализации, целостности данных и т. Д.?

Добавлено: Почти забыл сказать - у каждого из них более дюжины свойств, поэтому поместить их в одну таблицу с половиной полей, равных NULL, - довольно неудобное решение. Кроме того, ссылка может быть разорвана и воссоздана с другим объектом в любое время.

Ответы [ 9 ]

7 голосов
/ 13 ноября 2009

Чтобы решить эту проблему, я бы начал со стандартного макета отношения «многие ко многим».

TableA
  AId
  AInfo

TableB
  BId
  BInfo

TableA2B
  AId
  BId

Я бы тогда использовал триггеры, уникальные индексы, ограничения для обеспечения отношения 1: 1 в таблице. Точный метод будет зависеть от потребностей системы.

Причина, по которой мне нравится этот формат, заключается в том, что многие отношения имеют атрибуты как часть отношений, которые не являются частью сущностей. Эта схема учитывает это сейчас и в будущем.

Например: человек работает в компании. У отношений есть дата проката, которая не соответствует юридическому лицу или компании.

2 голосов
/ 13 ноября 2009
CREATE TABLE A (id INT NOT NULL PRIMARY KEY, field1, …)

CREATE TABLE B (id INT NOT NULL PRIMARY KEY, field1, …)

CREATE TABLE AB (aid INT NOT NULL, bid INT NOT NULL,
                CONSTRAINT pk_ab PRIMARY KEY (aid, bid),
                CONSTRAINT ux_a UNIQUE (aid), 
                CONSTRAINT ux_b UNIQUE (bid),
                CONSTRAINT fk_aid_a FOREIGN KEY (aid) REFERENCES A,
                CONSTRAINT fk_bid_b FOREIGN KEY (bid) REFERENCES B
                )
2 голосов
/ 13 ноября 2009

Я думаю, что схема будет выглядеть так:

create table A (
    A_id    integer    primary key,
    ...
);

create table B (
    B_id    integer    primary key,
    A_id    integer    references A (A_id),
    ...
);

alter table B add constraint c1 unique(A_id);

B может ссылаться только на одну строку в A, и поскольку поле уникально, на A может ссылаться только одна строка в B.

B.A_id имеет значение NULL, поэтому в A и B могут существовать строки, которые не ссылаются друг на друга.

Уникальное ограничение не препятствует существованию нескольких пустых записей. Уникальное ограничение гарантирует, что все значения являются либо уникальными, либо NULL.

0 голосов
/ 18 марта 2011
0 голосов
/ 14 ноября 2009

"Внешний ключ на обоих концах. Это также неудобно, когда вы пытаетесь установить / разорвать связь. Приходится прибегать к триггерам и прочим вещам. Я надеялся на более элегантное оружие."

Вы не найдете такого элегантного оружия во вселенной систем на основе SQL.

Ответ Дэррила Петерсона показывает логически правильное решение. Но некоторые случаи «изменения ссылки» могут стать кошмаром в SQL из-за отсутствия поддержки концепции TTM «множественное назначение».

0 голосов
/ 13 ноября 2009

ИМО рассматривает два разных случая. Первый случай лучше всего представить в моногамном браке: два объекта создаются независимо, и в какой-то момент они объединяются; позже они могут быть отсоединены и, возможно, соединены с другими объектами. Для таких отношений я бы предложил подход таблицы A2B, используемый многими другими здесь.

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

0 голосов
/ 13 ноября 2009

Я бы использовал решение, предложенное Дэррилом:

TableA
  AId
  AInfo

TableB
  BId
  BInfo

TableA2B
  AId
  BId

, а затем просто добавьте уникальное ограничение на AId в таблицеA2B и BId в таблицеA2B

alter TableA2B add constraint ucAId unique(AId)
alter TableA2B add constraint ucBId unique(BId)

Думаю, это решит твою проблему

Записи tableA, которые не связаны с записями tableB, просто не будут присутствовать в TableA2B, аналогично, записи tableB не связаны с tableA.

Ограничения приведут в исполнение максимум одну ссылку из таблицы A в таблицу B или из таблицы B в таблицу A

0 голосов
/ 13 ноября 2009

Отдельная таблица соединения ссылок внешнего ключа A и ссылки внешнего ключа B, оба столбца с ограничением UNIQUE. Таким образом, либо существует связь между двумя объектами, и она является единственной ссылкой для любого из них, либо ссылка не существует, поэтому в таблице нет строки.

0 голосов
/ 13 ноября 2009

Обнуляемый внешний ключ с уникальным ограничением на одном или обоих концах в зависимости (на обоих концах интересно!)

...