Сайт Макса Пантюхина

Главная
Обо мне
Ссылки
Фотографии
SQL и не только
Резюме
Предыдущая
Следующая
Р - значит ракета

      

      

Хранение родственных отношений в реляционной СУБД

Задача: В базе необходимо хранить информацию о людях и родственных связях между ними. Такая задача может использоваться при создании базы например "Детский сад", "Генеалогическое древо" и другое, где нужно хранить информацию о семье.

Решение "в лоб":

Недостатки такого решения сразу понятны, надо следить, чтобы вторая связь устанавливалась автоматически, например, при установки связи от мужа к жене под названием супруга, автоматически устанавливалась связь от жены к мужу причем под названием супруг. Т.е. придется заносить в таблицу Тип_связи дополнительное поле, показывающее какая связь к какой является зеркальной. Ну это еще не самое страшное, интересней что к связи отец зеркальными будут две связи дочь и сын. И при этом же к дочь -- отец и мать. С этим-то как разбираться?

Мне лично по душе
Другое решение:

В этом случае в таблицу Тип_связи добавляются два поля, ссылающихся на эту же таблицу (Visio отказался мне эту связь рисовать, так что я на словах) и указывающие, какая связь является зеркальной для персоны указанной в Связи.Код_человека, если она мужского рода и если женского. Теперь следить за второй связью не придется. Но, так как зеркальную связь надо будет рассчитывать, то при отображении связей в формах лучше воспользоваться представлением.

Когда я реализовывал это в MS-Access, то сделал на самом деле два представления. Одно рассчитывало только зеркальные связи:

Q_RelRev

SELECT IIf(Person.Пол=1,DRR.Relation_TypeReversM,DRR.Relation_TypeReversF)
       AS RelReversType,
       PersonRelations.Person_ID_Slave AS MyPerson,
       Person.Family+" "+Person.Name+" "+Person.Second_Name AS PFIO,
       PersonRelations.PersRel_ID, PersonRelations.D_Start, PersonRelations.D_Stop
  FROM ((Dic_Relations AS DRR INNER JOIN PersonRelations ON
         DRR.Relation_Type=PersonRelations.Relation_Type)
  INNER JOIN Person ON
             PersonRelations.Person_ID_Master=Person.Person_ID)
 
INNER JOIN Person AS PersonS ON
             PersonRelations.Person_ID_Slave=PersonS.Person_ID;

И второе, которое объединяло прямые и зеркальные связи:

SELECT Dic_Relations.Relation_Label AS RelType ,
       PersonRelations.Person_ID_Master AS MyPerson,
       Person.Family + " " + Person.Name + " " + Person.Second_Name AS PFIO,
       PersonRelations.PersRel_ID, PersonRelations.D_Start, PersonRelations.D_Stop
  FROM (Dic_Relations INNER JOIN PersonRelations ON
        Dic_Relations.Relation_Type = PersonRelations.Relation_Type)
   INNER JOIN Person ON
        PersonRelations.Person_ID_Slave = Person.Person_ID;
 UNION
SELECT Dic_Relations.Relation_Label AS RelType ,Q_RelRev.MyPerson , Q_RelRev.PFIO,
       Q_RelRev.PersRel_ID,Q_RelRev.D_Start, Q_RelRev.D_Stop
  FROM Q_RelRev INNER JOIN Dic_Relations ON
       Q_RelRev.RelReversType = Dic_Relations.Relation_Type;

На всякий случай - поясню:

Person - таблица Человек
Dic_Relations - таблица Тип_связи
PersonRelations - таблица Связи
PersonRelations.Person_ID_Master - Связи.Код_человека
PersonRelations.Person_ID_Slave - Связи.Код_связуемого
В запросах присутствуют еще два поля, о которых ничего не говорилось: PersonRelations.D_Start и PersonRelations.D_Stop - эти поля необходимы для того, чтобы отслеживать срок действия связи (например был мужем, а потом развелся, а потом второй муж появился)

 

 

 

Hosted by uCoz