2009-09-11 1 views
2

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

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).

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

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

+0

Это пахнет плохим дизайном. Можете ли вы быть более осведомлены о том, что такое данные и почему вы хотите связать их таким образом? – spender

+0

Похоже, вы пытаетесь создать условный внешний ключ. Как упоминалось выше, ощущение того, что вы пытаетесь достичь, поможет нам определить наилучший подход. –

ответ

4

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

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

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), группа индивидуальных пользователей (Т2), или группу групп (T3) ,

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

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

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


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

+0

Я не понимаю, почему T0 в этом примере является обязательным? И это не решает проблему с разными типами ключей. Я что-то пропустил? – Khanzor

+0

@Bill: Это потрясающая презентация. Думаю, мне просто нужно использовать прокси для обработки информации с помощью другого ключа. Спасибо за помощь! – Khanzor

+0

@ Хорошая презентация! –

0

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

1

Если DB имеет проверочные ограничения, можно взбивать некрасивый кладж как:

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

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

Перейти к веб-статьям о «обобщающем реляционном моделировании специализации». Там есть отличные.