Получить непосредственного потомка с максимальным возрастом для каждого родительского узла? - PullRequest
1 голос
/ 12 января 2020

Как я могу получить только старшего сына каждого родителя, если у меня есть N родителей, у каждого из которых N детей? У родителя может быть несколько детей, но у каждого ребенка может быть только один родитель.

Например:

Table 1 (Parents)
- John
- Maria
- Steve
Table 2 (Children)
- Bob, 8
- Sandra, 10
- Steve, 20
- Mario, 25
- Lucas, 50
- Sam, 2
- Ricardo, 7
Table 3 (Parents_Children)
- John | Bob, 8
- John | Sandra, 10
- John | Steve, 20
- Maria | Mario, 25
- Maria | Lucas 50
- Steve | Sam 2
- Steve | Ricardo, 7

Я хочу назначить встречу, возвращающую 100% из моих родителей, но только самый старший ребенок.

Результат должен быть:

- John | Steve, 20
- Maria | Lucas 50
- Steve | Ricardo 7

Я получаю результат, когда я ищу только с одним родителем:

select top 1 
    p.Name, 
    c.Name, 
    c.age 
from Parents as p
join Parent_Children as pc on pc.ParentId = p.Id
join Children as c on c.Id = pc.ChildrenId
where 
    p.Name = 'John'
order 
    by c.age desc

Но я хотел одним запросом вернуть этот результат всем родителям.

Ответы [ 4 ]

2 голосов
/ 12 января 2020

В ваших данных образца нет половой информации, поэтому я интерпретирую слова «старший сын» как «старший ребенок».

Вы можете использовать оконные функции:

select c.*
from (select pc.parentid, c.*,
             row_number() over (partition by pc.parentid order by c.age desc) as seqnum
      from Parent_Children pc join
           Children c
           on c.Id = pc.ChildrenId
     ) c
where seqnum = 1;

В случае близнецы (или другие множественные рождения), это возвращает одного произвольно. Чтобы вернуть все из них, используйте rank() вместо row_number().

0 голосов
/ 12 января 2020
;with cte as (
    select p.parent_name, c.child_name, c.age
        , rank() over (partition by parent_name order by c.age desc) as Rank
    from Parents p
    inner join Parent_Children pc on pc.pid = p.pid
    inner join Children c on c.cid = pc.cid
)
select parent_name, child_name, age
from cte
where Rank = 1;

Rank() или Dense_Rank(), обе функции могут использоваться здесь для получения желаемого результата, но в идеале у таблицы Children должен быть дополнительный столбец как Parent_Id с внешним ключом как PId из родительской таблицы.

Это уменьшит необходимость иметь третью таблицу и сохранит одно соединение.

0 голосов
/ 12 января 2020

Попробуйте следующее:

declare @parents table (pid int, parent_name varchar(100))
insert into @parents select 1, 'John'
insert into @parents select 2, 'Maria'
insert into @parents select 3, 'Steve'

--select * from @parents

declare @children table (cid int, child_name varchar(100), age int)
insert into @children select 1, 'Bob', 8
insert into @children select 2, 'Sandra', 10
insert into @children select 3, 'Steve', 20
insert into @children select 4, 'Mario', 25
insert into @children select 5, 'Lucas', 50
insert into @children select 6, 'Sam', 2
insert into @children select 7, 'Ricardo', 7

--select * from @children

declare @parent_children table (pid varchar(100), cid varchar(100), age int)
insert into @parent_children select 1 , 1, 8
insert into @parent_children select 1 , 2, 10
insert into @parent_children select 1 , 3, 20
insert into @parent_children select 2, 4, 25
insert into @parent_children select 2, 5, 50
insert into @parent_children select 3, 6, 2
insert into @parent_children select 3, 7, 7

--select * from @parent_children

;with cte
as
(
    select p.parent_name, 
        c.child_name, 
        c.age,
        rank() over (partition by parent_name order by c.age desc) rnk
    from @parents as p
    join @parent_children as pc on pc.pid = p.pid
    join @children as c on c.cid = pc.cid
)
select parent_name, child_name, age
from cte
where rnk = 1
0 голосов
/ 12 января 2020

Вот способ получить это:

with cte as
(     
    select 'John' as Parent, 'Bob, 8' as Children
    union select 'John', 'Sandra, 10'
    union select 'John', 'Steve, 20'
    union select 'Maria', 'Mario, 25'
    union select 'Maria', 'Lucas, 50'
    union select 'Steve', 'Sam, 2'
    union select 'Steve', 'Ricardo, 7'
),
-- add age as column
cte2 as 
(
    select Parent, Children, cast(right(Children, len(Children) - CHARINDEX(',', Children , 1)) as int) as Age
    from cte 
),
-- cte2 is to get the max age per Parent
cte3 as
(
    select Parent, max(Age) as Age
    from cte2 a
    group by Parent
)
select a.Parent, Children
from cte2 a
inner join cte3 b on a.Parent = b.Parent and a.Age = b.Age   
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...