We need the following tables:
People indicating their immediate father and mother:
create table people( id int identity not null, -- ID человека fio varchar(200) not null, -- ФИО father int, -- ID отца mother int, -- ID матери half int, -- Жена/Муж (текущие, если нужны прошлые - усложняйте структуру), sex tinyint, -- Пол: 1-Женщина, 2-Мужчина constraint pk_people primary key (id) );
Relationships :
create table relRule( rid int identity not null, -- ID отношения direct tinyint not null, -- Направление шага: 1-Родители, 2-Дети, 3-Братья, 4-Супруг mask tinyint not null default(3), -- Маска ветви: 1-Женская, 2-Мужская, 3-Любая xmask tinyint not null default(3),-- Маска исключительности: 1-Сводные, 2-Родные, 3-Все (родные и сводные) -- Имеет значение только для движения "вбок" (сестры/братья) prev_rid int null, -- Предыдущее отношение в дереве wname varchar(64) null, -- Родственник женщина (название) mname varchar(64) null, -- Родственник мужчина (название) comment varchar(64) null, -- Комментарий constraint pk_relRule primary key (rid) );
The tree of relationships is built in the form of ... a tree. At the top are the entries with prev_rid=NULL - the closest relatives. Any branch can lead in one of four directions: to parents, to children, to brothers, to a spouse. The name of the relative found for it, a man and a woman, is stored on the branch separately. If through this branch one has to go only along the male or female branch, then such a branch is divided into two, indicating in the field mask gender of the relative. In this case, in the name of a relative, it makes sense to fill only one of the fields (according to the floor of the record). For example, a wife and a husband are made separate branches, because their relatives are called completely differently (father-in-law, mother-in-law, mother-in-law, father-in-law). In difficult cases, service branches may appear without names of relatives on them, employees just getting to the next nodes, in this case it is recommended to indicate the purpose of the branch in the comment field.
To move on a tree to brothers and sisters there is an additional sign "Exclusivity" ( xmask ), it defines both parents must be the same or only one. For the separation of relatives and consolidated.
Example of a relationship tree:
direct=Родители(1), mask=Оба(3) = Мать/Отец direct=Братья(3), mask=Оба(3), xmask=Родные(2) | = Тетя/Дядя direct=Дети(2),mask=Оба(3) | | = Двоюродные Сестра/Брат direct=Родители(1), mask=Оба(3) | = Бабушка/Дедушка direct=Супруг(4), mask=Мужчина(2) = Муж direct=Родители(1), mask=Оба(3) = Свекровь/Свекр direct=Братья(3), mask=Оба(3) = Золовка/Деверь
But the query that is looking for relatives in this structure:
with Q(rel_name,P,id,fio,father,mother,half,sex,rid,mask,xmask) as ( select Cast(NULL as varchar(64)),0 P,P1.*,0 rid,Cast(0 as tinyint) mask,Cast(3 as tinyint) xmask from people P1 where P1.id=13 -- <<-- Для кого ищем родственников union all select * from -- <<-- Рекурсивная часть запроса ( select choose(P2.sex,R.wname,R.mname) rel_name, case when P1.father=P2.father and P1.mother=P2.mother then 2 else 1 end | case R.direct when 3 then 0 else 3 end P, P2.*,R.rid,R.mask,R.xmask from Q,people P1,people P2,relRule R where P1.id=Q.id and ((R.prev_rid is null and Q.rid=0) or R.prev_rid=Q.rid) and ( (R.direct=1 and P2.id in(P1.father,P1.mother)) or(R.direct=2 and (P2.father=P1.id or P2.mother=P1.id)) or(R.direct=3 and (P1.father=P2.father or P1.mother=P2.mother)) or(R.direct=4 and P2.id=P1.half) ) and P1.id!=P2.id and R.mask & P2.sex>0 -- Проверка пола ветви ) A where AP & A.xmask > 0 -- Проверка Родной/Сводный ) select Q.* from Q where rel_name is not null
SQLFIDDLE An example of a sample of degrees of relationship. A large example of a kinship tree is also presented here (I had to translate it, otherwise fiddle did not display Russian :()