Один ко многим самому себе

Как можно структурировать таблицу для объекта, который может иметь отношение один ко многим к себе? В частности, я работаю над приложением для отслеживания разведения животных. У каждого животного есть удостоверение личности; у него также есть идентификатор производителя и идентификатор дамы. Таким образом, возможно иметь одного ко многим от отца или дамы до его потомства. Я был бы склонен к чему-то вроде этого:

ID INT NOT NULL PRIMARY KEY
SIRE_ID INT 
DAME_ID INT

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

Так:

  1. Может кто-нибудь указать мне на статью / веб-страницу, которая обсуждает моделирование такого рода отношений?
  2. Идентификатор должен быть INT или чем-то вроде String? NULL в INT будет означать, что у животного нет родителей в базе данных, но String со специальными значениями флага может использоваться для указания того же самого.
  3. Будет ли это лучше всего моделировать через две таблицы? Я имею в виду одну таблицу для животных и отдельную таблицу, указывающую исключительно родство, например:

    животное

    ID INT НЕ НУЛЬ ПЕРВИЧНЫЙ КЛЮЧ

    родство

    ID INT НЕ НУЛЬ ПЕРВИЧНЫЙ КЛЮЧ ИНОСТРАННЫЙ КЛЮЧ

    SIRE_ID INT ПЕРВИЧНЫЙ КЛЮЧ ИНОСТРАННЫЙ КЛЮЧ

    DAME_ID INT ПЕРВИЧНЫЙ КЛЮЧ ИНОСТРАННЫЙ КЛЮЧ

Прошу прощения за вышесказанное: мой SQL ржавый. Я надеюсь, что это как бы передает то, о чем я думаю.

9 ответов

Решение

Что ж, это "нормальное" отношение "один ко многим", и метод, который вы предлагаете, является классическим для его решения.

Обратите внимание, что две таблицы денормализованы (я не могу точно указать, где находится часть "супер-ключ-не-хорошо-должен-быть-подмножество-другого-ключа-fsck-I-забыл", но я уверен, что это где-то там); Интуитивно понятная причина в том, что кортеж в первом соответствует максимум кортежу во втором, поэтому, если у вас нет большого количества животных с нулевыми идентификаторами отца и дамы, это не очень хорошее решение в любой перспективе (это ухудшает производительность - нужно объединение - и не снижает требования к хранению).

Я думаю, что ваш макет с использованием только одной таблицы в порядке. Вы определенно хотите сохранить SIRE_ID и DAME_ID в том же типе данных, что и ID. Вы также хотите объявить их как ЗАРУБЕЖНЫЕ КЛЮЧИ (возможно, что внешний ключ указывает на ту же таблицу, а внешний ключ также может быть нулевым).

ID INT NOT NULL PRIMARY KEY
SIRE_ID INT REFERENCES TABLENAME (ID)
DAME_ID INT REFERENCES TABLENAME (ID)

Используя этот макет, вы можете легко искать родительских животных, а также вы можете построить дерево потомства для данного животного (для Oracle есть CONNECT BY)

Я задавал подобный вопрос несколько месяцев назад на сайте MySQL. Я бы порекомендовал вам взглянуть на ответ, который я получил от Питера Броули относительно этого типа отношений: http://forums.mysql.com/read.php?135,187196,187196

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

Альтернативная предлагаемая архитектура (которая будет полностью нормализована) будет выглядеть примерно так:

Таблица: животное

ID | Имя | разводить

Таблица: родословная

animal_id | parent_id | parentType (сир или дама)

Я не знаю о животноводстве, но похоже, что ваш Sire_ID это отец, а Dame_ID это мать? Нет проблем. Одна строка для каждого животного, ноль sire_ и dame_ID для купленных животных, я не вижу никаких проблем.

[ID],[Sire_ID],[Dame_ID];
0,null,null  (male)
1,null,null  (female)
2,null,null  (female)
3,0,1 (male)
4,0,2 (male)
5,null,null  (female)
6,3,5
7,4,5

и так далее. Вы вероятно заполнили бы TreeView или XmlNodeList в цикле while...

While (myAnimal.HasChildren) {
 Animal[] children = GetChildren(Animal.ID)
 for (int x=0; x<children.length; x++) 
  myAnimal.Children.Add(children[x]);
}

В данном случае Animal.Children - это коллекция животных. Следовательно, myAnimal.Children[0].ather вернет myAnimal. .Parent[] может быть коллекцией двух его родителей, которая должна работать, если [0] всегда является одним родителем (отцом), а [1] всегда другим (матерью).

Сделайте ID идентификатором PK и назначьте Sire_ID и Dame_ID программно, вернув идентификаторы его родителей. Никакие связи с внешним ключом не должны быть необходимыми, хотя оба родительских идентификатора могут ссылаться на ID, если вы действительно этого хотите.

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

Я не вижу никакой выгоды в разделении дизайна на две таблицы.

Похоже, вы хотите построить что-то вроде дерева.

Как насчет чего-то вроде?

 ID          Primary Key,
 Parent_ID   Foreing_Key
 ( data )

Существует некоторая функциональность для выполнения запросов в таблицах с отношениями к себе. Смотрите синтаксис Connect By: http://www.adp-gmbh.ch/ora/sql/connect_by.html

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

Используйте предложение "connect by" с SQL, чтобы указать, какой иерархии следовать.

На самом деле это не отношения один ко многим, если только у животного не может быть много родителей.

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

Другие вопросы по тегам