2009-03-17 2 views
6

Учитывая таблицу моделей «A», которая может иметь несколько дочерних моделей «B», из которых «B» будет иметь одну или несколько дочерних моделей «C» .. это звучит просто, однако мне нужно обеспечить, чтобы для каждого «A» любой «B» должен иметь уникальный набор «C» .. например, C не может быть дочерним из двух «B», которые являются частью одного и того же родителя »A '.., но' C 'может быть ребенком из нескольких «B», учитывая, что каждый «родитель B» «A» различен.Лучший дизайн схемы для отношения таблицы, который обеспечивает целостность

Имеет ли это смысл или я должен обескуражить свой сценарий? приветствие заранее!

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

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

Просто чтобы прояснить ситуацию, я объясню сценарий, но вот некоторые примечания:

«A» имеет нулевую или более «B», а «B», неявно связанный с «A», и как таковая всегда является ребенком только одного «А». «C» - это нечто вроде корневой сущности, которая связана со многими «B», а также с другими элементами в базе данных.


Heres реальная история:

Это сайт, который содержит много записок (А), и многие члены (C), краткое может иметь множество представлений (B), из которых представление будет всегда есть один или несколько связанных членов. Идея состоит в том, что представление может фактически быть сотрудничеством, и каждый член не имеет больше «власти», чем любой другой, но будет существовать другая система для проверки политики взаимодействия членов.

Итак, в кратком изложении член может подавать только одно представление, и в представлении может быть много членов (соавторов).

Надеюсь, что это поможет, но я думаю, что вы уже много мне помогли!

Steve.

+0

+1 для того, чтобы СУБД принудительно применяла ограничение независимо от того, что делают приложения (приложения). –

+0

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

+0

Как видно из моего ответа, ваш сценарий довольно запутан, и если вы можете его изменить, вам следует. Число систем, поддерживающих CREATE ASSERTION, очень ограничено. Утверждение, требуемое для проверки ограничений, является сложным для использования в триггере. –

ответ

3

Я думаю, что вам нужно SQL стандартных утверждения, которые (к сожалению) в основном невыполненные фактическим СУБД.

всех ответов соглашается, что есть три первичные таблицы, называемые TableA, TableB и TableC, каждый из которых содержит свой собственный идентификатор колонки:

TableA (A_ID PRIMARY KEY, ...) 
TableB (B_ID PRIMARY KEY, ...) 
TableC (C_ID PRIMARY KEY, ...) 

это не ясно из описания проблемы, является ли одно значение B может иметь несколько родительских записей A. Понятно, что один C может иметь несколько родительских записей B. Если B привязан к одному A, конструкция TableB может быть пересмотрен:

TableB (B_ID, ..., A_ID REFERENCES TableA) 

Если B может быть связан с несколькими разными А-х, то соединение лучше всего представлены соединительной таблице:

Из описания также не ясно, должно ли C, связанное с B, быть одинаковым для каждого A, с которым связано B, или могут ли различные A ссылаться на один B и набор связанных с C с B для A1 может отличаться от набора C, связанного с B для A2. (Конечно, если один B может быть связан только с одним A, этот вопрос является спорным.)

Для целей этого ответа я собираюсь предположить, что любой B связан с одним A, поэтому структура TableB включает A_ID в качестве внешнего ключа. Так как один C может быть связан с несколькими Б, соответствующая структура нового присоединения таблицы:

B_and_C (B_ID REFERENCES TableB, 
     C_ID REFERENCES TableC, 
     PRIMARY KEY (B_ID, C_ID) 
     ) 

Упрощая (опуская правила о deferrability и непосредственности) утверждение выглядит следующим образом:

CREATE ASSERTION assertion_name CHECK (<search_condition>) 

Так , как только мы получим набор проектных решений, мы можем написать утверждение для проверки данных.Учитывая таблицу TableA, TableB (с внешним ключом A_ID), TableC и B_and_C требованием является то, что число вхождений данного C_ID через полный А 1.

CREATE ASSERTION only_one_instance_of_c_per_a CHECK 
(
    NOT EXISTS (
     SELECT A_ID, COUNT(C_ID) 
      FROM TableB JOIN B_and_C USING (C_ID) 
      GROUP BY A_ID 
      HAVING COUNT(C_ID) > 1 
    ) 
) 

[Измененная: Я думаю, что это является более точным:

CREATE ASSERTION only_one_instance_of_c_per_a CHECK 
(
    NOT EXISTS (
     SELECT A_ID, C_ID, COUNT(*) 
      FROM TableB JOIN B_and_C USING (C_ID) 
      GROUP BY A_ID, C_ID 
      HAVING COUNT(*) > 1 
    ) 
) 

]

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


В комментариях ниже, meandmycode примечание:

я получаю чувство, что есть ошибка в моей конструкции. Моя реальная логика заключается в том, что у «В» всегда есть хотя бы один ребенок «С». Это не имеет смысла, учитывая, что «B» должен существовать до того, как его ребенок может быть прикреплен. База данных в настоящее время позволяет привязывать «B» к «A», не имея хотя бы одного «C». Ребенок, я как таковой пересматриваю «B», так что у него есть поле, которое ссылается на его основной ребенок «C», а также наличие дочерней коллекции дополнительных «C», но теперь у меня есть коллекция, которая также может включать в себя первичный «C», указанный «B», что было бы неправильно.

Есть ли шаблон db, который выводит правило «одного или нескольких детей», против нуля или более?

У меня возникли проблемы с вашей моделью. Трудно создать B, если уже существует C, который ссылается на вновь созданный B, особенно если C должен относиться только к существующим B. На ум приходит фраза «курица и яйцо». Итак, обычно, вы разрешаете B иметь нулевой или более C в таком контексте.

Вы все еще не указали, есть ли у TableB внешний ключ A_ID или у вас есть таблица ссылок, такая как A_and_B. Если у него есть внешний ключ, то, предположительно, вы не можете создать B до тех пор, пока не создадите A, к которому он относится.

Я не думаю, что один C ID в таблице B является хорошей идеей - это делает для асимметричной обработки (сложнее SQL). Это также означает, что если вам нужно удалить один C, вам необходимо обновить все, чтобы одна из других ссылок на C была удалена из таблицы, в которой она сейчас находится, а затем обновляет значение в записи B. Это грязно, чтобы быть вежливым об этом.

Думаю, вам нужно изменить свой вопрос, чтобы определить фактическую структуру таблицы, на которую вы смотрите, - по линиям, показанным в разных ответах; вы можете использовать тройные точки для представления других, но неуместных столбцов. Утверждение, которое я предложил, вероятно, должно быть реализовано как какой-то триггер, который попадает в специфичные для СУБД записи.


Из измененного описания трусах (А), представления (B) и членов (С), то ясно, что одно представление относится к только один краткий, так что материалы могут иметь простой внешний ключ, который идентифицирует краткое изложение, для которого это представление. Член может сотрудничать только в одном представлении для конкретного краткого сообщения. Будет таблица «submit_collaborators» с столбцами для идентификации представления и участника, комбинация является первичным ключом, а каждый столбец является внешним ключом.

Briefs(Brief_ID, ...) 
Submissions(Submission_ID, Brief_ID REFERENCES Briefs, ...) 
Members(Member_ID, ...) 
Submission_Collaborators(Submission_ID REFERENCES Submissions, 
         Member_ID REFERENCES Members, 
         PRIMARY KEY (Submission_ID, Member_ID) 
         ) 

Следовательно, требование состоит в том, что следующий запрос не должен возвращать никаких строк:

SELECT s.brief_id, c.member_id, COUNT(*) 
    FROM submissions AS s JOIN submission_collaborators AS c 
     ON s.submission_id = c.submission_id 
    GROUP BY s.brief_id, c.member_id 
    HAVING COUNT(*) > 1 

Это тот же запрос, который я встроен в CREATE утверждение (второй вариант). Вы также можете выделить дополнительную информацию (краткое название, название заявки, имя участника, различные даты и т. Д.), Но суть проблемы заключается в том, что указанный запрос не должен возвращать данные.

+0

Джонатан, спасибо за ответ, очень сжатый. У меня такое чувство, что это недостаток в моем дизайне, моя реальная логика в том, что у «Б» всегда есть хотя бы один ребенок «С» .. это не что «B» должен существовать до того, как его ребенок может быть прикреплен. * cont * – meandmycode

+0

База данных в настоящее время позволяет привязать «B» к «A», не имея хотя бы одного «C». Ребенок, я как таковой пересматриваю «B», так что у него есть поле это относится к его первому ребенку «C», а также к дочерней коллекции * cont * – meandmycode

+0

дополнительных «C», но теперь у меня есть коллекция, которая также может включать в себя первичный «C», указанный «B», который be .. wrong – meandmycode

0

Добавить идентификатор таблицыA в TableB и добавить его в первичный ключ и сделать то же самое для TableB и TableC.

редактировать:

Я считаю, что первая часть этого ответа будет работать на А-Б ограничения. Тем не менее, я тогда установил бы таблицу ссылок между B и C, которая также провела A PK. Таким образом, у вас есть 1: N между A: B, а затем ваши принудительные действия.

+0

Это не будет работать, потому что C может быть ребенком нескольких B, если они не делят A. –

+0

@Gamecat - The причина, по которой вы дали, что она не работает, - это именно тот вопрос, который мы пытаемся решить, и этот ответ неверен, но это потому, что нет ничего, что остановило бы всех Bs от наличия C1 в качестве ребенка. – cdeszaq

-1

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

+0

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

+0

@cdeszaq: в какой СУБД вы это сделаете - и как бы вы сделали это без триггера? –

0

У вас есть тройные отношения. Что вам нужно сделать, это иметь таблицу, которая связывает A и B и C вместе в ее первичном ключе. Поскольку первичные ключи не могут быть дублированы, это будет обеспечивать наличие только одного C для каждого A, а также каждого B. Это создаст уникальную коллекцию, которую вы искали.

Вы получаете следующую структуру таблицы:

A's({A_ID}, ...) 
B's({B_ID}, ...) 
C's({C_ID}, ...) 
A_B_C_Relation({[A_ID], [B_ID], [C_ID]}, ...) 

Первичные ключи находятся в фигурных скобках, внешние ключи в скобках.

Посмотрите here для получения дополнительной информации.

+0

Как это принуждает «C не может быть дочерним из двух» B, которые являются частью одного и того же родителя «A»? – Jon

+0

Это не является достаточно ограничительным: он позволяет {A = 1, B = 1, C = 1} и {A = 1, B = 2, C = 1}, но правила говорят, что это недопустимо. –

3

Я думаю, что у меня есть модель отношений, захваченная здесь; Если нет, то я голосую за unobfuscation:

  • A [{AID}, ...]
  • B [{BID}, AID, ...]
  • C [{CID}, .. .]
  • B_C_Link [{БИД, ИДС}, АИД]
    • Дополнительный уникальный индекс (AID, ИДС)

В нотации используется индикатор {} первичного ключа. Так как может иметь несколько Bs (путем размещения AID на B), Bs может иметь Cs (используя таблицу «много ко многим» B_C_Link), и несколько Cs не могут принадлежать одному и тому же A (добавив AID к многоточечной таблице B_C_Link) ко-многим таблицы и исполнившим (Aid, ИДС) уникальность.

+0

У вас есть избыточность в таблице B_C_Link. AID можно найти из значения BID, чтобы ваша таблица не была в BCNF. На самом деле, я думаю, что это просто в 1NF, даже не 2NF, поскольку BID -> AID является транзитивной зависимостью. Если вы принимаете эту избыточность, то дополнительный уникальный индекс работает относительно чисто. –

+0

Правильно, это связано с денормализацией базы данных. Как говорится: «Нормализовать, пока это не повредит, денормализовать, пока он не сработает». –

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