For MySQl 8.0+ you can use ROW_NUMBER()
window function to rank member names inside their groups alphabetically and after filtering only the first 3 names of each group use conditional aggregation to get the result that you want:
SELECT group_name,
MAX(CASE WHEN rn = 1 THEN name END) Member1,
MAX(CASE WHEN rn = 2 THEN name END) Member2,
MAX(CASE WHEN rn = 3 THEN name END) Member3
FROM (
SELECT group_name,
CONCAT(lastname, ' ', firstname) name,
ROW_NUMBER() OVER (PARTITION BY group_name ORDER BY lastname, firstname) rn
FROM tbl_Participants
) t
WHERE rn <= 3
GROUP BY group_name
For prior versions use aggregation with GROUP_CONCAT()
and SUBSTRING_INDEX()
to split the names list:
SELECT group_name,
SUBSTRING_INDEX(names, ',', 1) Member1,
CASE WHEN counter > 1 THEN SUBSTRING_INDEX(SUBSTRING_INDEX(names, ',', 2), ',', -1) END Member2,
CASE WHEN counter > 2 THEN SUBSTRING_INDEX(SUBSTRING_INDEX(names, ',', 3), ',', -1) END Member3
FROM (
SELECT group_name,
COUNT(*) counter,
GROUP_CONCAT(CONCAT(lastname, ' ', firstname) ORDER BY lastname, firstname) names
FROM tbl_Participants
GROUP BY group_name
) t
See a simplified demo.
If there are groups in tbl_Groups
with no rows in tbl_Participants
and you want them in the results, it's easier to do it with UNION ALL
than a join.
Add to the above queries:
UNION ALL
SELECT g.group_name, null, null, null
FROM tbl_Groups g
WHERE NOT EXISTS (
SELECT 1
FROM tbl_Participants p
WHERE p.group_name = g.group_name
)