0

I'm attempting to combine multiple rows into individual rows with multiple columns, where the data is from three tables. I followed the suggestions in MySQL pivot table but the question and answers do not account for multiple joins.

Here are the underlying tables:

table n
+------+----+------+
| name | id | code |
+------+----+------+
| foo  |  1 | NULL |
| bar  |  2 | z    |
| baz  |  3 | y    |
+------+----+------+

table ac
+------+----+
| code | id |
+------+----+
| h    |  1 |
| i    |  2 |
+------+----+

table c
+-----+------+-------+
| cid | code | desc  |
+-----+------+-------+
|   9 | h    | desch |
|   9 | i    | desci |
|   8 | z    | descz |
|   8 | y    | descy |
+-----+------+-------+

Here are the expected results:

+------+-------+-------+
| name | type8 | type9 |
+------+-------+-------+
| foo  | null  | desch |
| bar  | descz | desci |
| baz  | descy | null  |
+------+-------+-------+

I can get pretty close to the results I want with:

select 
n.name,
n.code as type8,
ac.code as type9
from n
left join ac
on ac.id=n.id

but as expected, this yields the codes only:

+------+-------+-------+
| name | type8 | type9 |
+------+-------+-------+
| foo  | null  | h     |
| bar  | z     | i     |
| baz  | y     | null  |
+------+-------+-------+

and I'm interested in replacing the codes with the longer descriptions from table c.

enharmonic
  • 1,800
  • 15
  • 30

2 Answers2

0

The first step is to use case to return multiple columns of data from multiple rows within the same table using the technique from pivot tables in mysql:

select
n.name,
case when c.cid=8 then c.desc end as type8,
case when c.cid=9 then c.desc end as type9
from n
left join c  
on n.code=c.code 

This yields the correct results for type8 but nulls for type9:

+------+-------+-------+
| name | type8 | type9 |
+------+-------+-------+
| foo  | null  | null  |
| bar  | descz | null  |
| baz  | descy | null  |
+------+-------+-------+

The next step is to get the results for type9:

select
n.name,
case when c.cid=8 then c.desc end as type8,
case when c.cid=9 then c.desc end as type9
from n
left join ac
on ac.id=n.id
left join c
on c.code=ac.code

This yields:

+------+-------+-------+
| name | type8 | type9 |
+------+-------+-------+
| foo  | null  | desch |
| bar  | null  | desci |
| baz  | null  | null  |
+------+-------+-------+

If these two results are unioned together with

select 
n.name,
case when c.cid=8 then c.desc end as type8,
case when c.cid=9 then c.desc end as type9
from n
left join c  
on n.code=c.code

union

select
n.name,
case when c.cid=8 then c.desc end as type8,
case when c.cid=9 then c.desc end as type9
from n
left join ac
on ac.id=n.id
left join c
on c.code=ac.code

the rows still need to be combined:

+------+-------+-------+
| name | type8 | type9 |
+------+-------+-------+
| bar  | descz | null  |
| baz  | descy | null  |
| foo  | null  | null  |
| foo  | null  | desch |
| bar  | null  | desci |
| baz  | null  | null  |
+------+-------+-------+

Finally, I was able to achieve the expected results by using the aggregating technique for removing nulls from a unioned query:

select 
n.name,
min(type8), min(type9)
from
(select 
n.name,
case when c.cid=8 then c.desc end as type8,
case when c.cid=9 then c.desc end as type9
from n
left join c  
on n.code=c.code

union
select
n.name,
case when c.cid=8 then c.desc end as type8,
case when c.cid=9 then c.desc end as type9
from n
left join ac
on ac.id=n.id
left join c
on c.code=ac.code) as n
group by n.name

which yields the results as expected:

+------+-------+-------+
| name | type8 | type9 |
+------+-------+-------+
| foo  | null  | desch |
| bar  | descz | desci |
| baz  | descy | null  |
+------+-------+-------+
enharmonic
  • 1,800
  • 15
  • 30
  • 1
    Hm. For tips on acquiring a better answer, see https://meta.stackoverflow.com/questions/333952/why-should-i-provide-a-minimal-reproducible-example-for-a-very-simple-sql-query - but equally, I think you could simply study your own linked solutions a little more thoroughly. – Strawberry Jun 01 '19 at 06:25
  • @Strawberry Thanks for the link. I would be happy if you would suggest a better answer; here's a [fiddle](https://www.db-fiddle.com/f/v4VCgd3XH9SRNjkzFJEvQM/0) with the schema. – enharmonic Jun 03 '19 at 16:05
0

Another way to get the desired results is to create a pseudo union by combining ifnull(.,.) with 2 left join statements and then aggregating the cid's to mimic a pivot.

select name
    , min(if(cid = 8, `desc`, null)) Type8
    , min(if(cid = 9, `desc`, null)) Type9
from (
    select cid, code, `desc`, ifnull(name1, name2) name from (
        select c.cid, c.code, c.desc, n1.name name1, n2.name name2
        from c
        left join n n1 on n1.code = c.code
        left join ac on ac.code = c.code
        left join n n2 on n2.id = ac.id
    ) q1
) p1
group by name
order by name
;