I have two tables. I need to join them, but I have a doubt since these two tables do not have a common column name.
people
id | name | gender |
---|---|---|
107 | Days | F |
145 | Hawbaker | M |
155 | Hansel | F |
202 | Blackston | M |
227 | Criss | F |
278 | Keffer | M |
305 | Canty | M |
329 | Mozingo | F |
425 | Nolf | M |
534 | Waugh | F |
586 | Tong | M |
618 | Dimartino | F |
747 | Beane | M |
878 | Chatmon | F |
904 | Hansard | F |
relations
c_id | p_id |
---|---|
145 | 202 |
145 | 107 |
278 | 305 |
278 | 155 |
329 | 425 |
329 | 227 |
534 | 586 |
534 | 878 |
618 | 747 |
618 | 904 |
Desired output:
child | father | mother |
---|---|---|
Dimartino | Beane | Hansard |
Hawbaker | Blackston | Days |
Keffer | Canty | Hansel |
Mozingo | Nolf | Criss |
Waugh | Tong | Chatmon |
My approach:
select distinct
people.name as child,
people.name as father, people.name as mother
from
relations
inner join
people on relaions.c_id = people.id
This is the output of this query:
child | father | mother |
---|---|---|
Dimartino | Dimartino | Dimartino |
Hawbaker | Hawbaker | Hawbaker |
Keffer | Keffer | Keffer |
Mozingo | Mozingo | Mozingo |
Waugh | Waugh | Waugh |
How to get the desired output in order of the name of the child?
CodePudding user response:
Join the people table twice, once for the parent, once for the child. Then group by child and use conditional aggregation to get father and mother.
select
c.name as child,
max(case when p.gender = 'M' then p.name end) as father,
max(case when p.gender = 'F' then p.name end) as mother
from relations r
join people p on p.id = r.p_id
join people c on c.id = r.c_id
group by c.id, c.name
order by c.name;
And just to show that this is also possible of course without aggregation (and to show an approach that may come easier to mind when you are a beginner):
with fathers as
(
select p.name, r.c_id
from relations r
join people p on p.id = r.p_id
where p.gender = 'M'
)
, mothers as
(
select p.name, r.c_id
from relations r
join people p on p.id = r.p_id
where p.gender = 'F'
)
, children as
(
select c.name, c.id as c_id
from people c
where c.id in (select r.c_id from relations r)
)
select
children.name as child,
fathers.name as father,
mothers.name as mother
from children
left join mothers on mothers.c_id = children.c_id
left join fathers on fathers.c_id = children.c_id
order by children.name;
I am using outer joins here for the case that a child's mother or father is unknown (i.e. not in the relation table).
CodePudding user response:
Nested joins should work neatly. No CTEs or grouping necessary.
- Left join
relations
... - ... and within that, inner join
people
who have genderF
- Repeat again for gender
M
- Exclude any who already have children
select
c.name as child,
father.name as father,
mother.name as mother
from people c
left join relations rm
join people mother on mother.id = rm.p_id and mother.gender = 'F'
on rm.c_id = c.id
left join relations rf
join people father on father.id = rf.p_id and father.gender = 'M'
on rf.c_id = c.id
where c.id not in (
select r.p_id
from relations r
);