Можно ли ограничить таблицу иметь значение только в одном из набора столбцов - PullRequest
2 голосов
/ 11 сентября 2009

У меня есть таблица, которая должна связать одну из трех отдельных таблиц, но она должна ссылаться только на одну из них, например,

Main_Table
id UNIQUEIDENTIFIER
t1_id UNIQUEIDENTIFIER
t2_id INT
t3_id INT

T1
id UNIQUEIDENTIFIER
name VARCHAR(255)

T2
id INT
name VARCHAR(255)

T3
id INT
name VARCHAR(255)

Возможно ли иметь ограничение, при котором только один из t1, t2 или t3 не может быть нулевым в любой момент времени?

Это просто плохой дизайн? Если да, то что бы вы посоветовали для дизайна?

EDIT:

Меня попросили уточнить причины этого конкретного проекта.

Main_Table пытается быть таблицей плательщика, которая может ссылаться либо на отдельного пользователя (T1), либо на группу отдельных пользователей (T2), либо на группу групп (T3).

Это дизайн базы данных, который я унаследовал, и к сожалению, он не подлежит изменению.

Моя самая большая проблема заключается в том, что мне нужно связать разные типы, поэтому поле типа здесь работать не будет, так как индексы разные.

Ответы [ 4 ]

4 голосов
/ 11 сентября 2009

Описываемый вами дизайн называется эксклюзивные дуги . Да, это довольно хрупкий дизайн и даже не соответствует некоторым правилам нормализации.

Вот альтернатива:

Main_Table
id UNIQUEIDENTIFIER
t_id INT NOT NULL
  FOREIGN KEY (t_id) REFERENCES T0 (id)

T0
id UNIQUEIDENTIFIER
type INT NOT NULL CHECK (type IN (1,2,3))
  UNIQUE KEY (id, type)

T1
id INT 
type INT NOT NULL CHECK (type = 1) 
name VARCHAR(255)
  FOREIGN KEY (id, type) REFERENCES T0 (id, type)

T2
id INT
type INT NOT NULL CHECK (type = 2)
name VARCHAR(255)
  FOREIGN KEY (id, type) REFERENCES T0 (id, type)

T3
id INT
type INT NOT NULL CHECK (type = 3)
name VARCHAR(255)
  FOREIGN KEY (id, type) REFERENCES T0 (id, type)

При таком дизайне каждая строка в Main_Table должна ссылаться на одну строку в T0.
Аналогично, каждая строка в T0 может быть родительской только для одной строки в T1, T2 или T3.

Это способ реализации наследования таблиц классов и полиморфных ассоциаций без нарушения ссылочной целостности.


Main_Table пытается быть плательщиком таблица, которая может ссылаться либо на индивидуальный пользователь (T1), группа отдельные пользователи (T2) или группа группы (Т3).

Правильно, так что подумайте об этом с точки зрения объектно-ориентированного дизайна. Если бы у вас было три класса, которые могли бы функционировать в качестве получателя платежей, вы бы создали интерфейс с именем Payable или что-то подобное, чтобы каждый из них мог полагаться на ввод этих объектов. Например, все объекты Payable должны иметь метод sendPayment(). В некоторых языках ОО интерфейс является суперклассом и называется абстрактный класс или чистый виртуальный класс .

Таблица T0 функционирует как общий тип для каждой из дочерних таблиц T1, T2 и T3. Когда Main_Table имеет внешний ключ для T0, это все равно что сказать, что Main_Table должен иметь ссылку на некоторую сущность, которая является Payable, но любой объект, происходящий из этого суперкласса, можно использовать.

Столбец type - это всего лишь хитрость, позволяющая убедиться, что на данный T0.id может ссылаться только одна таблица подкласса за раз. Это не обязательно, если вы можете положиться на логику своего приложения, чтобы вставить данную дочернюю строку только в одну из таблиц подкласса.


См. Также раздел Полиморфные ассоциации в моей презентации " SQL Antipatterns Strike Back ."

1 голос
/ 11 сентября 2009

Если у вашей БД есть проверочные ограничения, вы могли бы создать уродливую кучу вроде:

ALTER TABLE Main_Table
 add constraint CK_ThisWorksButItsUgly
  check ( (  case when t1_id is null then 0 else 1 end
           + case when t2_id is null then 0 else 1 end
           + case when t3_id is null then 0 else 1 end) = 1)

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

Исключительные дуги Билла Карвина очень крутые, если вы можете изменить архитектуру базы данных.

0 голосов
/ 11 сентября 2009

Это еще один пример шаблона gen-spec.

Go ogle веб-статьи на тему "Обобщение реляционного моделирования специализации" Там есть несколько отличных.

0 голосов
/ 11 сентября 2009

Помимо плохого дизайна, если вы не можете изменить его, можно использовать триггер для применения этого ограничения

...