Home » excel » mysql – Join the first 3 records in each group, and merge as one row

mysql – Join the first 3 records in each group, and merge as one row

Posted by: admin May 14, 2020 Leave a comment

Questions:

One user has many qualifications. I need to produce a spreadsheet in Excel from a MySQL database. The result should contain a row for each user, with columns for their 1st, 2nd and 3rd qualifications on the same row.

Users Table

id  |  Name     
--------------
1   |  James
2   |  Bob
3   |  Adam
4   |  Michael

Qualifications Table

id  |  user_id  |  subject
-----------------------------------------
1   |  1        |  English
2   |  2        |  History
3   |  2        |  Mathematics
4   |  2        |  Biology
5   |  2        |  Sports Science
6   |  2        |  Art
7   |  3        |  Physics
8   |  3        |  Chemistry
10  |  4        |  Geography
11  |  4        |  Computer Science
12  |  4        |  Theology

DESIRED RESULT

User ID  |  Name     |  Qualification 1  |  Qualification 2   |  Qualification 3
-----------------------------------------------------------------------
1        |  James    |  English          |  NULL              |  NULL
2        |  Bob      |  History          |  Mathematics       |  Biology
3        |  Adam     |  Physics          |  Chemistry         |  NULL
4        |  Michael  |  Geography        |  Computer Science  |  Theology

Notes

  • I only need the first 3 qualifications for each user.
  • The user could have any number of qualifications. They may have none, they may have 10.
  • In the example, James only has 1 qualification. Bob has 5. Adam only has 2.
  • I only need a SQL solution, but the end goal is to export to Excel (not relevant unless there is something I can leverage in Excel to make this simpler).
  • This is a one-time task. I won’t need to do this regularly.
How to&Answers:

The simplest method (pre-MySQL 8.0) uses variables and conditional aggregation:

select u.user_id, u.name,
       max(case when rn = 1 then q.subject end) as qualification_1,
       max(case when rn = 2 then q.subject end) as qualification_2,
       max(case when rn = 3 then q.subject end) as qualification_3
from users u left join
     (select q.*,
             (@rn := if(@u = q.user_id, @rn + 1,
                        if(@u := q.user_id, 1, 1)
                       )
             ) as rn
      from qualifications q,
           (select @u := -1, @rn := 0) params
      order by q.user_id, q.id
     ) q
     on u.id = q.user_id
where rn <= 3
group by u.id, u.name

Answer:

You can use GROUP_CONCAT and SUBSTRING_INDEX functions. The three subjects could appear in one column:

SELECT
    users.id,
    users.name,
    SUBSTRING_INDEX(GROUP_CONCAT(qualifications.subject SEPARATOR ','), ',', 3) AS subjects
FROM users
LEFT JOIN qualifications ON users.id = qualifications.user_id
GROUP BY users.id, users.name

Or you can separate them:

SELECT
    id,
    name,
    subjects,
    SUBSTRING_INDEX(subjects, ',', 1) AS subject1,
    SUBSTRING_INDEX(SUBSTRING_INDEX(subjects, ',', 2), ',', -1) AS subject2,
    SUBSTRING_INDEX(SUBSTRING_INDEX(subjects, ',', 3), ',', -1) AS subject3
FROM (
    SELECT
        users.id,
        users.name,
        CONCAT(GROUP_CONCAT(qualifications.subject SEPARATOR ','), ',,') AS subjects
    FROM users
    LEFT JOIN qualifications ON users.id = qualifications.user_id
    GROUP BY users.id, users.name
) AS x