2016-09-28 4 views
2

Рассмотрим ситуацию, когда я определить объект, группу объектов, затем таблицу, которая связывает их вместе:Внешний ключ к таблице А или В

CREATE TABLE obj (
    id INTEGER PRIMARY KEY, 
    name text 
) ; 

CREATE TABLE group (
    id INTEGER PRIMARY KEY ; 
    grpname TEXT 
) ; 

CREATE TABLE relation (
    objid INTEGER, 
    grpid INTEGER, 
    PRIMARY KEY (objid, grpid) 
) ; 

Я ищу каскадное удаление, когда это применимо, так что я добавить внешний ключ

ALTER TABLE relation 
ADD FOREIGN KEY (objid) 
REFERENCES obj(id) 
ON DELETE CASCADE ; 

ALTER TABLE relation 
ADD FOREIGN KEY (grpid) 
REFERENCES group(id) 
ON DELETE CASCADE ; 

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

CREATE TABLE relation_ver1 (
    parent INTEGER, 
    child INTEGER, 
    PRIMARY KEY (parent, child) 
) ; 
ALTER TABLE relation_ver1 
ADD FOREIGN KEY (parent) 
REFERENCES group(id) 
ON DELETE CASCADE ; 

Здесь я получаю вопрос: Я хотел бы применить каскадное удаление для ребенка, но я не знаю, здесь, если ребенок относится к группе или объекту ,

Можно ли добавить внешний ключ в таблицу obj или группу?

Единственное решение, которое я нашел, - это добавление полей child_obj и child_grp, добавление относительных внешних ключей, а затем при вставке, например, в объект используется специальная (тип нулевой) группы и обратная операция, когда вставка подгруппы.

+0

Ваше требование несовместимо с понятием «ключа» в теории отношений. решение, о котором вы упомянули, уже хорошо, я думаю. – ultrajohn

+0

Любые намеки на суррогаты? @ultrajohn – marom

+0

, чтобы предотвратить разреженность ваших данных в таблице relation_ver1, я предлагаю вам отделить случай, когда дочерний объект является объектом, а ребенок - группой в свои собственные таблицы. – ultrajohn

ответ

1

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

Это означает, что grpid объявлен как REFERENCES group(id), чтобы гарантировать, что grpid никогда не получит значения, которое не найдено в группе (id). Таким образом, это вопрос действительности. Каскадный DELETE также сводится к действительности: если ключ удаляется, то любые и все внешние ключи, относящиеся к этому ключу, будут оставлены недействительными, поэтому четко, что-то должно быть сделано о них. Каскадное удаление является одним из возможных решений. Установка внешнего ключа на NULL, тем самым лишая связь, является еще одним возможным решением.

Ваше понятие о наличии дочернего идентификатора относится к группе или объекту, нарушая любое понятие ссылочной целостности. Теория реляционной базы данных не имеет смысла и не предусматривает полиморфизма. Ключ должен относиться к одному и только одному виду сущности. Если нет, то вы начинаете сталкиваться с такими проблемами, как тот, который вы только что обнаружили, но еще хуже, вы не можете иметь никаких гарантий ссылочной целостности в своей базе данных. Это не очень хорошая ситуация.

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

CREATE TABLE tree_relation (
    parent_id INTEGER, 
    child_object_id INTEGER, 
    child_group_id INTEGER, 
    PRIMARY KEY (parent_id, child_object_id, child_group_id)); 
ALTER TABLE tree_relation 
    ADD FOREIGN KEY (parent_id) REFERENCES group(id) ON DELETE CASCADE; 
ALTER TABLE tree_relation 
    ADD FOREIGN KEY (child_object_id) REFERENCES object(id) ON DELETE CASCADE; 
ALTER TABLE tree_relation 
    ADD FOREIGN KEY (child_group_id) REFERENCES group(id) ON DELETE CASCADE; 

Все, что вам нужно сделать, это убедиться, что только один из child_object_id, child_group_id не является NULL.

+0

Хм, как-то лучше моего обходного пути, так как я могу избавиться от «нулевого объекта/группы». Могу ли я выразить ограничение child_object_id ИЛИ child_group_id, а не NULL в SQL? – marom

+0

Я не думаю, что вы можете, но я совсем не уверен в этом. Это может быть отличным предметом для другого вопроса о стеке. –

+0

По стандарту SQL вы можете это сделать, указав «CHECK» на основе tuple. Альтернативно, в случае, если '' CHECK'' не поддерживается в вашей СУБД, вы можете определить TRIGGER для достижения того же результата. – ultrajohn

1

Рассмотрим соотношение:

relation_ver1(parent, child_obj, child_group) 

Я утверждаю, что это отношение имеет следующие недостатки:

  • Вы должны иметь дело с NULL частный случай.
  • Приблизительный. 1/3 значений NULL. Значения NULL плохие.

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

relation_ver_obj(parent, child_obj) и relation_ver_grp(parent, child_group).

Смежные вопросы