I’m working with SQL Workbench. This is an example of the tables I have:
SITES id BUILDINGS id site_id LEVELS id building_id
I have not created this DB and can’t modify it. Each building corresponds to a site, so it contains a KEY to such site. Same with levels and buildings.
I am trying to get this result
site_id | buildings_id | levels_id 1 | 15, 16 | (213, 214), (313, 314) 2 | 21, 22, 23 | (350), (400, 401, 402), (501) 3 | 31, 32, 33, 34 | (5001, 5002), (7001), (8009), (8400)
the first two columns are easy, but I’m having issues with the 3rd. The parenthesis are illustrative, I don’t necessarily need those. But I need a way to split the string into levels by building_id.
Each (…) corresponds to the levels in one building.
This is the query I have working now which returns the first and second column. I have tried adding another LEFT OUTER JOIN to the query, but didn’t work. I tried many things but most of the things I try return errors or weird outputs.
SELECT sites.id AS site_id, GROUP_CONCAT(DISTINCT buildings.id SEPARATOR '; ') AS buildings_id FROM sites LEFT OUTER JOIN users_has_sites ON users_has_sites.sites_id = sites.id LEFT OUTER JOIN users ON users.id = users_has_sites.users_id LEFT OUTER JOIN buildings ON buildings.sites_id = sites.id WHERE users.id = 42 GROUP BY site_id;
Any help is appreciated. Thanks
Advertisement
Answer
You need two levels of aggregation: one by building_id
and another by site_id
.
with b_l as ( select b.id as building_id , b.site_id , concat('(' , group_concat( l.id order by l.id separator ',' ) , ')' ) as building_levels from buildings as b left join levels as l on b.id = l.building_id group by b.id, b.site_id ) , s_b as ( select s.id , group_concat( b_l.building_id order by b_l.building_id separator ',' ) as buildings , group_concat( b_l.building_levels order by b_l.building_id separator ',' ) as levels from sites as s left join b_l on s.id = b_l.site_id group by s.id ) select * from s_bid | buildings | levels -: | :-------- | :---------------------- 1 | 1,2,3 | (1,2),(3,4,5),(6) 2 | 4,5,6 | (7,8),(9),(10,11) 3 | 7,8,9 | (12,13,14),(15,16),(17) 4 | 10 | null
Or the same with lateral join:
select s.id , group_concat( b_l.building_id order by b_l.building_id separator ',' ) as buildings , group_concat( b_l.building_levels order by b_l.building_id separator ',' ) as levels from sites as s left join lateral ( select b.site_id , b.id as building_id , concat('(' , group_concat( l.id order by l.id separator ',' ) , ')' ) as building_levels from buildings as b left join levels as l on b.id = l.building_id group by b.id, b.site_id ) b_l on s.id = b_l.site_id group by s.id
db<>fiddle here