Home > database >  How to create MySQL query that converts rows to columns?
How to create MySQL query that converts rows to columns?

Time:09-09

I'm creating an employee tracking app.

I have a MySQL table which is as follows:

ID PersonID TypeID DateTime
1 001 IN 2022-09-01T13:21:12
2 001 OUT 2022-09-01T13:25:12
3 001 IN 2022-09-01T14:21:12
4 001 OUT 2022-09-01T14:25:12
5 002 IN 2022-09-03T13:21:12
6 002 OUT 2022-09-03T13:25:12
7 002 IN 2022-09-03T14:21:12
8 002 IN 2022-09-03T14:25:12
9 002 OUT 2022-09-03T14:25:12
10 002 OUT 2022-09-03T16:25:12
11 002 OUT 2022-09-03T17:25:12
12 002 IN 2022-09-04T16:25:12
13 002 IN 2022-09-05T17:25:12

I would like to create a view that returns records first sorted by PersonID and then by the ID but transforms the rows into columns.

Something like this:

PersonID InID In_DateTime OutID Out_DateTime
001 1 2022-09-01T13:21:12 2 2022-09-01T13:25:12
001 3 2022-09-01T14:21:12 4 2022-09-01T14:25:12
002 5 2022-09-03T13:21:12 6 2022-09-03T13:25:12
002 7 2022-09-03T14:21:12 null null
002 8 2022-09-03T14:25:12 9 2022-09-03T14:25:12
002 null null 10 2022-09-03T16:25:12
002 null null 11 2022-09-03T17:25:12
002 12 2022-09-04T16:25:12 null null
002 13 2022-09-05T17:25:12 null null

Does anyone have an idea how to do this in MySQL?

Thanks for any suggestions.

CodePudding user response:

Use window functions LEAD() or LAG() to get for each row its pair row, depending on its TypeID and do a left join of the results to the table:

WITH cte AS (
  SELECT *, 
     CASE 
       WHEN TypeID = 'IN' AND LEAD(TypeID) OVER w = 'OUT' THEN LEAD(ID) OVER w
       WHEN TypeID = 'OUT' AND LAG(TypeID) OVER w = 'IN' THEN LAG(ID) OVER w
     END other_ID  
  FROM tablename
  WINDOW w AS (PARTITION BY PersonID ORDER BY DateTime)
)
SELECT DISTINCT c.PersonID,
       CASE WHEN c.TypeID = 'IN' THEN c.ID ELSE t.ID END InID,
       CASE WHEN c.TypeID = 'IN' THEN c.DateTime ELSE t.DateTime END In_DateTime,
       CASE WHEN c.TypeID = 'IN' THEN t.ID ELSE c.ID END OutID,
       CASE WHEN c.TypeID = 'IN' THEN t.DateTime ELSE c.DateTime END Out_DateTime
FROM cte c LEFT JOIN tablename t
ON t.ID = c.other_ID
ORDER BY c.PersonID, COALESCE(In_DateTime, Out_DateTime);

See the demo.

  • Related