Отношение 1 к 1 точно с одной из нескольких таблиц - PullRequest
2 голосов
/ 13 апреля 2011

Хорошо, я надеюсь, что смогу прояснить, в чем моя проблема: у меня есть база данных с 5 таблицами.Давайте назовем их A и B, V_1, V_2 и V_3.А и В представляют собой список того, что нужно сделать.Эти действия описаны в таблицах V_i.Теперь А представляет собой своего рода шаблон материала, который должен быть сделан с определенным типом элемента.B, с другой стороны, описывает, что должно быть сделано (или было сделано) с конкретным экземпляром, если абстрактный элемент описан A. Таким образом, в терминологии ООП можно сказать, что A представляет класс, а B представляет экземпляр AВсякий раз, когда что-то вставляется в таблицу B, связанные данные из таблицы A копируются, чтобы их можно было изменить для этого конкретного элемента, не влияя на A.

Хорошо, так вот реальная проблема: Как мнемодель это правильно?Моя главная проблема заключается в том, что каждая запись в V_i не должна быть связана как с A, так и с B. Это должна быть связь 1: 1 с ЛИБО A или B. Кроме того, V_i и V_j не должны быть связаны с одной и той же записью в A илиБ. Я понятия не имею, как сделать это правильно.Текущая структура выглядит следующим образом:

A и B имеют PK с именем ID.Каждый V_i также имеет PK с именем ID и два FK, которые обозначают A или B, давайте назовем их A_ID и B_ID.Теперь текущая реализация гарантирует, что A_ID или B_ID имеют значение NULL, но не оба.Однако мне было интересно, есть ли лучший способ сделать это.Кроме того, существует проблема, связанная с тем, что несколько V_i могут ссылаться на одну и ту же запись в A или B.

Итак, я надеюсь, что моя проблема ясна.Есть ли способ правильно смоделировать это с реляционными базами данных, не полагаясь на внешний код для обеспечения соблюдения ограничений?Спасибо за ваш вклад заранее.

С уважением, Дэвид

Ответы [ 4 ]

2 голосов
/ 13 апреля 2011

В теории отношений взаимно-однозначные отношения обычно переводятся в одну таблицу в физической модели. Эта отдельная таблица будет содержать строки из обеих таблиц, и вы будете использовать проверочные ограничения для определения типа строки. На сегодняшний день это самый простой способ получить надежные отношения 1: 1.

1 голос
/ 13 апреля 2011

Первое: при проектировании базы данных вы выражаете отношения между записями, а не таблицами. Вы выражаете свою проблему с точки зрения ОО. Эту парадигму нельзя использовать для разработки таблиц (SQL является декларативным языком).

В противном случае вы можете добавить ограничения на вашу таблицу, гарантируя ваш предикат.

Возможно, Oracle предлагает другие возможности, которых я не знаю.

0 голосов
/ 13 апреля 2011

Предисловие: Это плохой дизайн, как уже отмечали другие.

Допущения:

create table a (a_id number primary key); 

create table b (b_id number primary key); 

create table v1 
(v1_id number primary key, a_id number references a, b_id number references b);

create table v2
(v2_id number primary key, a_id number references a, b_id number references b);

create table v3
(v3_id number primary key, a_id number references a, b_id number references b);

Обязательный в любом изV_i таблицы, в которых требуется ровно один из идентификаторов из A или B (но не оба), довольно просты.

alter table V1
  add constraint v1_check check
  (    (a_id is null and b_id is not null)
    or (a_id is not null and b_id is null)
  );

Если вы хотите расширить это ограничение, чтобы точно один из идентификаторов изA или B присутствует, и это значение существует в одной и только одной строке:

create unique index v1_check_unique on v1  ( coalesce (a_id, b_id) );

Сложная задача - убедиться, что идентификаторы из A и B существуют в одной и только одной из таблиц V_i,Это не может быть сделано во время DML, но оно может быть применено во время фиксации.

create materialized view log on v1 with rowid;
create materialized view log on v2 with rowid;
create materialized view log on v3 with rowid;
CREATE MATERIALIZED VIEW CROSS_TABLE
REFRESH FAST ON COMMIT
AS
  SELECT V1_ID AS V_ID, 'V1' AS TABLE_NAME, ROWID AS ROW_ID, 
         COALESCE (A_ID, B_ID) AS OTHER_ID FROM V1
  UNION ALL
  SELECT V2_ID AS V_ID, 'V2' AS TABLE_NAME, ROWID AS ROW_ID, 
         COALESCE (A_ID, B_ID) AS OTHER_ID FROM V2
  UNION ALL
  SELECT V3_ID AS V_ID, 'V3' AS TABLE_NAME, ROWID AS ROW_ID, 
         COALESCE (A_ID, B_ID) AS OTHER_ID FROM V3
/

ALTER TABLE CROSS_TABLE ADD CONSTRAINT CROSS_TABLE_UNIQUE UNIQUE (OTHER_ID);

Это работает, но не так круто, как вы надеетесь.Oracle не может применить эту уникальность для всех таблиц во время оператора времени, поскольку сеанс А не может учитывать любые другие изменения, которые могут вносить другие сеансы.Эта уникальность может быть применена только в commit time.

Следующий тестовый пример завершается неудачно при запуске с пустыми таблицами - и откатывает всю транзакцию, так как не может определить, что вызываетотказ.Будьте бдительны.

INSERT INTO A VALUES (1);
INSERT INTO B VALUES (1);
INSERT INTO V1 (V1_ID, A_ID, B_ID) VALUES (1, 1, NULL);
INSERT INTO V2 (V2_ID, A_ID, B_ID) VALUES (1, 1, NULL);
COMMIT;
0 голосов
/ 13 апреля 2011

Наиболее распространенный способ моделирования отношений класса с экземпляром в rdbs: Класс = таблица Экземпляр = строка

Подумайте об этом: вы вставляете новую строку для каждого нового экземпляра; там, где вы не вставляете данные, вставляются значения по умолчанию, которые дают вам данные класса; и триггеры дают вам поведение на уровне класса.

В качестве альтернативы, присвойте A и B один и тот же первичный ключ и установите для PK из B значение FK для PK из A. Когда строка включена в B, СУБД проверит, существует ли «родительская» строка в A. Вероятно, нужно нарисовать

+--------+   +--------+
|Table A |   |Table B |
+--------+   +--------+
|id (PK) |<--|id* (PK)|
|col1    |   |colB1   |
| ...    |   | ...    |
+--------+   +--------+
...