robin g
robin g

Reputation: 61

Categorizing in select statement

So i have this table

id     |   object    |     type
--------------------------------
1      |   blue      |     color
1      |   burger    |     food
2      |   sandwich  |     food
2      |   red       |     color
2      |   coke      |     beverage 
3      |   sprite    |     beverage
3      |   coke      |     beverage
3      |   red       |     color
4      |   bacon     |     food

i have to create a select statement that will show a table with columns id, color, food and beverage. Arranged by ID with their designated things on it.

so my expected result is

id       |   color   |   food   |  beverage
-------------------------------------------
1        |   blue    |   burger |
2        |   red     | sandwich |   coke
3        |   red     |          |   sprite
3        |           |          |   coke
4        |           |  bacon   |

as of now i have this code

Select id as id,
Case When I.Type = 'color' Then I.Object End As color, 
Case When I.Type = 'food' Then I.Object End As food, 
Case When I.Type = 'beverage' Then I.Object End As beverage
From table I
order by id

but the problem with my code is it doesnt group by its ID so it creates multiple rows for every object.

TIA!

Upvotes: 2

Views: 160

Answers (4)

user5683823
user5683823

Reputation:

Robin: My comment to you (under Tim Biegeleisen's answer) is partially incorrect. There IS a pivot-based solution; however, the "groups" are not by id, but instead they are by id AND rank within your three "categories". For this solution (or ANY solution that does not use dynamic SQL) to work, it is necessary that all the "types" (and their names) be known beforehand, and they must be hardcoded in the SQL query.

NOTE: In this solution, I assumed that for each id, the "objects" within the same "type" are associated to each other based on their alphabetical order (so, for example, for id = 3, "coke" is associated with "red" and "sprite" is associated with NULL, unlike your sample output). I asked you about that right below your Question - if there are additional rules you did not share with us, requiring a different pairing of objects of different types, it may or may not be possible to adapt the solution to meet those additional rules.

EDIT: On closer look, this is pretty much what Aleksej provided, without using the explicit pivot syntax. His solution has the advantage that it would work in older versions of Oracle (before 11.1 where pivot first became available).

QUERY (including test data in the first CTE):

with
     inputs ( id, object, type ) as (
       select 1, 'blue'     , 'color'    from dual union all
       select 1, 'burger'   , 'food'     from dual union all
       select 2, 'sandwich' , 'food'     from dual union all
       select 2, 'red'      , 'color'    from dual union all
       select 2, 'coke'     , 'beverage' from dual union all
       select 3, 'sprite'   , 'beverage' from dual union all
       select 3, 'coke'     , 'beverage' from dual union all
       select 3, 'red'      , 'color'    from dual union all
       select 4, 'bacon'    , 'food'     from dual 
     ),
     r ( id, object, type, rn ) as (
       select id, object, type, row_number() over (partition by id, type order by object)
       from   inputs
     )
select id, color, food, beverage
from   r
  pivot ( max(object) for type in ( 'color'    as color, 'food' as food, 
                                    'beverage' as beverage))
order by id, rn
;

OUTPUT:

  ID COLOR    FOOD     BEVERAGE
---- -------- -------- --------
   1 blue     burger
   2 red      sandwich coke
   3 red               coke
   3                   sprite
   4          bacon

Upvotes: 0

Aleksej
Aleksej

Reputation: 22959

You can try with something like the following:

with test(id, object, type) as
(
    select 1,'blue',    'color' from dual union all
    select 1,'burger',  'food' from dual union all
    select 2,'sandwich','food' from dual union all
    select 2,'red',     'color' from dual union all
    select 2,'coke',    'beverage' from dual union all 
    select 3,'sprite',  'beverage' from dual union all
    select 3,'coke',    'beverage' from dual union all
    select 3,'red',     'color' from dual union all
    select 4,'bacon',   'food' from dual
)
select id,
       max( case when type = 'color'    
              then object
              else null
            end
          ) as color,
       max( case when type = 'food'    
              then object
              else null
            end
          ) as food,
       max( case when type = 'beverage'    
              then object
              else null
            end
          ) as beverage
from (
        select id, object, type, row_number() over ( partition by id, type order by object) row_for_id
        from test
     )
group by id, row_for_id   
order by id, row_for_id

The inner query is the main part, where you handle the case of a single id with many objects of a type; you can modify the ordering by editing the order by object. The external query can be re-written in different ways, for example with a PIVOT; i used the MAX hoping to make it clear.

Upvotes: 1

Sanjay Radadiya
Sanjay Radadiya

Reputation: 1286

Try this

I have achieved using pivot clause

select id,object,type from yourtable
pivot
(
     LISTAGG(object, ',') WITHIN GROUP (ORDER BY object)
     for type IN
               (
                  'color'  AS "color",
                  'food'   AS "food",
                  'beverage' AS "beverage"
               )
)
order by id

Upvotes: 0

Tim Biegeleisen
Tim Biegeleisen

Reputation: 522346

You are looking for a pivot query. What is challenging about your problem is that, for a given id and type, there can be more than one object present. To handle this, you can first do a GROUP BY query to CSV aggregate objects for a given type using LISTAGG:

SELECT id,
       MAX(CASE WHEN t.type = 'color'    THEN t.object ELSE NULL END) AS color,
       MAX(CASE WHEN t.type = 'food'     THEN t.object ELSE NULL END) AS food,
       MAX(CASE WHEN t.type = 'beverage' THEN t.object ELSE NULL END) AS beverage
FROM
(
    SELECT id,
           LISTAGG(object, ',') WITHIN GROUP (ORDER BY object) AS object,
           type
    FROM yourTable
    GROUP BY id, type
) t
GROUP BY t.id

The inner query first aggregates objects across both id and type, and the outer query is a simple pivot query as you might expect.

Here is a Fiddle which shows an almost identical query in MySQL (Oracle seems to be perpetually broken):

SQLFiddle

Upvotes: 1

Related Questions