简体   繁体   中英

Combine multiple rows into one column

I am trying to run a query to fetch me slots a worker is free. The SQL Schema for ' works ' table is as follows:

+------------+----------+------------+----------+
|   workID   | workerID | workerType | workTime |
+------------+----------+------------+----------+
| 0000000001 |        1 | agents     |        1 |
| 0000000002 |        1 | agents     |        2 |
| 0000000003 |        1 | agents     |        4 |
| 0000000004 |        1 | agents     |        4 |
+------------+----------+------------+----------+

Another table ' worker ' is as:

+----------+------------+
| workerID | workerName |
+----------+------------+
|        1 | John Doe   |
+----------+------------+

I need the output as follows:

+------------+-----------+-----------+-----------+-----------+
| workerType | timeslot1 | timeslot2 | timeslot3 | timeslot4 |
+------------+-----------+-----------+-----------+-----------+
| John Doe   | Occupied  | Occupied  | Free      | Occupied  |
+------------+-----------+-----------+-----------+-----------+

But by my query, if I GROUP BY wo.workerID then I get

+------------+-----------+-----------+-----------+-----------+
| workerName | timeslot1 | timeslot2 | timeslot3 | timeslot4 |
+------------+-----------+-----------+-----------+-----------+
| John Doe   | Free      | Free      | Free      | Occupied  |
+------------+-----------+-----------+-----------+-----------+

MY TRY:

SQLFIDDLE

SELECT wo.workerName,
   CASE
    WHEN w.workTime = 1 THEN 'Occupied'
    ELSE 'Free'
   END AS timeSlot1,
   CASE
    WHEN w.workTime = 2 THEN 'Occupied'
    ELSE 'Free'
   END AS timeSlot2,
   CASE
    WHEN w.workTime = 3 THEN 'Occupied'
    ELSE 'Free'
   END AS timeSlot3,
    CASE
    WHEN w.workTime = 4 THEN 'Occupied'
    ELSE 'Free'
   END AS timeSlot4
FROM works AS w
INNER JOIN workers AS wo ON wo.workerID = w.workerID
WHERE w.workerType = 'agents' AND
  w.workerID = 1
GROUP BY wo.workerID;

Just add max() function for pivoting

SELECT wo.workerName,
       max( CASE
        WHEN w.workTime = 1 THEN 'Occupied'
        ELSE 'Free'
       END) AS timeSlot1,
       max(CASE
        WHEN w.workTime = 2 THEN 'Occupied'
        ELSE 'Free'
       END) AS timeSlot2,
       max(CASE
        WHEN w.workTime = 3 THEN 'Occupied'
        ELSE 'Free'
       END) AS timeSlot3,
        max(CASE
        WHEN w.workTime = 4 THEN 'Occupied'
        ELSE 'Free'
       END) AS timeSlot4
FROM works AS w
INNER JOIN workers AS wo ON wo.workerID = w.workerID
WHERE w.workerType = 'agents' AND
      w.workerID = 1

http://www.sqlfiddle.com/#!9/33230/11

SELECT wo.workerName,
       IF(SUM(IF(w.workTime = 1,1,0)),'Occupied','Free') AS timeSlot1,
       IF(SUM(IF(w.workTime = 2,1,0)),'Occupied','Free') AS timeSlot2,
       IF(SUM(IF(w.workTime = 3,1,0)),'Occupied','Free') AS timeSlot3,
       IF(SUM(IF(w.workTime = 4,1,0)),'Occupied','Free') AS timeSlot4
FROM works AS w
INNER JOIN workers AS wo ON wo.workerID = w.workerID
WHERE w.workerType = 'agents' AND
      w.workerID = 1
GROUP BY w.workerID

Answer from @Abhik Chakraborty may be working for this particular case, but if you ever change the strings for example Occupied will become Busy you are going to get Free as a result from MAX() .

This is because MAX() compares strings on a char-by-char basis. You should assign an integer value and then apply a function on it.

His method would fail: Whenever Occupied 's first character would be smaller than Free character. For example rename Occupied to Busy .

-- How it works right now

 select greatest('occupied','free');
 greatest 
----------
 occupied

-- How the behaviour would change if we rename occupied to free

select greatest('busy','free');
 greatest 
----------
 free

To prevent this case it is safer to use this method:

SELECT
    workerName, 
    CASE WHEN timeSlot1 = 1 THEN 'Occupied' ELSE 'Free' END AS timeSlot1,
    CASE WHEN timeSlot2 = 1 THEN 'Occupied' ELSE 'Free' END AS timeSlot2,
    CASE WHEN timeSlot3 = 1 THEN 'Occupied' ELSE 'Free' END AS timeSlot3,
    CASE WHEN timeSlot4 = 1 THEN 'Occupied' ELSE 'Free' END AS timeSlot4
FROM(
    SELECT 
        wo.workerName,
        MAX(CASE WHEN w.workTime = '1' THEN 1 END) AS timeSlot1,
        MAX(CASE WHEN w.workTime = '2' THEN 1 END) AS timeSlot2,
        MAX(CASE WHEN w.workTime = '3' THEN 1 END) AS timeSlot3,
        MAX(CASE WHEN w.workTime = '4' THEN 1 END) AS timeSlot4
    FROM                                      
        works AS w
        INNER JOIN workers AS wo ON wo.workerID = w.workerID
    WHERE 
        w.workerType = 'agents' AND
        w.workerID = 1
    GROUP BY wo.workerName
    ) foo;

Important Note

Your workTime column should not be type of varchar(n) as well. This is error prone because it compares strings by characters.

select greatest('211'::varchar, '29'::varchar);
 greatest 
----------
 29

I don't think it would be your expected result.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM