Ordinary Motorist
Ordinary Motorist

Reputation: 41

Oracle SQL: Transform rows to multiple columns

I'm using Oracle 11G and need a way to turn rows into new groups of columns in a select statement. We're transitioning to a 1:3 relationship for some of our data and need a way to get it into a view. Can you help us transform data that looks like this:

+---------+------------+
| User_Id | Station_Id |  
+---------+------------+  
|       1 |        203 |
|       1 |        204 |
|       2 |        203 |
|       3 |        487 |
|       3 |       3787 |
|       3 |        738 |
+---------+------------+

into this:

+---------+-------------+-------------+---------------+
| User_Id | Station_One | Station_Two | Station_Three |
+---------+-------------+-------------+---------------+
|       1 |         203 | 204         | Null          |
|       2 |         203 | Null        | Null          |
|       3 |         487 | 3787        | 738           |
+---------+-------------+-------------+---------------+

Let me know what ever other specifics you would like and thank you for any help you can give!

Upvotes: 1

Views: 216

Answers (2)

David Faber
David Faber

Reputation: 12485

The easiest way to accomplish this in my experience is to use conditional aggregation:

WITH mydata AS (
    SELECT 1 AS user_id, 203 AS station_id FROM dual
     UNION ALL
    SELECT 1 AS user_id, 204 AS station_id FROM dual
     UNION ALL
    SELECT 2 AS user_id, 203 AS station_id FROM dual
     UNION ALL
    SELECT 3 AS user_id, 487 AS station_id FROM dual
     UNION ALL
    SELECT 3 AS user_id, 3787 AS station_id FROM dual
     UNION ALL
    SELECT 3 AS user_id, 738 AS station_id FROM dual
)
SELECT user_id
     , MAX(CASE WHEN rn = 1 THEN station_id END) AS station_one
     , MAX(CASE WHEN rn = 2 THEN station_id END) AS station_two
     , MAX(CASE WHEN rn = 3 THEN station_id END) AS station_three
  FROM (
    SELECT user_id, station_id, ROW_NUMBER() OVER ( PARTITION BY user_id ORDER BY rownum ) AS rn
      FROM mydata
) GROUP BY user_id;

Just replace the mydata CTE in the above query with whatever your table's name is:

SELECT user_id
     , MAX(CASE WHEN rn = 1 THEN station_id END) AS station_one
     , MAX(CASE WHEN rn = 2 THEN station_id END) AS station_two
     , MAX(CASE WHEN rn = 3 THEN station_id END) AS station_three
  FROM (
    SELECT user_id, station_id, ROW_NUMBER() OVER ( PARTITION BY user_id ORDER BY rownum ) AS rn
      FROM mytable
) GROUP BY user_id;

Upvotes: 1

void
void

Reputation: 7890

You can use row_number and self joins:

with cte as 
(
 select userid, stationid,
        row_number() over(partition by userid order by stationid) rn
 from tbl
)

select distinct c1.userid,
                c1.stationid station_one,
                c2.stationid station_two,
                c3.stationid station_three 
from cte c1
left join cte c2 on c1.userid=c2.userid and c2.rn=2
left join cte c3 on c1.userid=c3.userid and c3.rn=3
where c1.rn=1

See the demo

You can also do it with row_number and subqueries:

with cte as 
(
 select userid, stationid,
        row_number() over(partition by userid order by stationid) rn
 from tbl
)

select distinct userid, 
       (select stationid from cte c where c.userid=cte.userid and c.rn=1) station_one,
       (select stationid from cte c where c.userid=cte.userid and c.rn=2) station_two,
       (select stationid from cte c where c.userid=cte.userid and c.rn=3) station_three
from cte

See the demo

Upvotes: 1

Related Questions