Home > Back-end >  SQL pivot with duplicate IDs to return earliest time
SQL pivot with duplicate IDs to return earliest time

Time:10-27

I have a table that looks like

ID Event Time
1 a 1:00 PM
1 a 1:05 PM
1 b 2:00 PM
1 c 3:00 PM
1 d 4:00 PM
1 d 4:02 PM
2 a 11:30
2 b 3:04 PM
2 b 3:06 PM
2 c 2:15 PM
2 c 5:20 PM
3 a 12:30 PM
3 b 4:00 PM
3 c 1:00 PM
4 c 10:00 PM

I have a code that pivots like

select * from

(

select

id

,event

,time

,row_number() over(partition by event order by time) as line

from table

)

pivot(min(time) for event in (a,b,c)) where line=1

;

I'm getting a table that returns only the IDs with earliest times per column per ID like below, with null values. It won't even return the 4th ID since it doesn't have the earliest time for event c. This is a large dataset with thousands of IDs. It looks like it is only returning the ID with the earliest time for the # of events. So if I have 5 event times, it will only return me with 5 IDs max with earliest time for one of the column. I'm hoping it makes sense.

ID a b c
1 (null) 2:00 PM (null)
2 11:30 (null) (null)
3 (null) (null) 1:00 PM

What I want is a table like above that returns the earliest time for each event per ID. Like below:

ID a b c
1 1:00 2:00 3:00
2 11:30 3:04 2:15

The table looks like it needs to be pivoted so this is what I've tried so far.

CodePudding user response:

How about conditional aggregation?

Setting date (time) format (you don't have to do that):

SQL> alter session set nls_date_format = 'hh24:Mi';

Session altered.

Sample data:

SQL> with test (id, event, time) as
  2    (select 1, 'a', to_date('13:00', 'hh24:mi') from dual union all
  3     select 1, 'a', to_date('13:05', 'hh24:mi') from dual union all
  4     select 1, 'b', to_date('14:00', 'hh24:mi') from dual union all
  5     select 1, 'c', to_date('15:00', 'hh24:mi') from dual union all
  6     select 1, 'd', to_date('16:00', 'hh24:mi') from dual union all
  7     select 1, 'd', to_date('16:02', 'hh24:mi') from dual union all
  8     --
  9     select 2, 'a', to_date('11:30', 'hh24:mi') from dual union all
 10     select 2, 'b', to_date('15:04', 'hh24:mi') from dual union all
 11     select 2, 'b', to_date('15:06', 'hh24:mi') from dual union all
 12     select 2, 'c', to_date('14:15', 'hh24:mi') from dual union all
 13     select 2, 'c', to_date('17:20', 'hh24:mi') from dual union all
 14     --
 15     select 3, 'a', to_date('12:30', 'hh24:mi') from dual union all
 16     select 3, 'b', to_date('16:00', 'hh24:mi') from dual
 17    )

Query begins here:

 18  select id,
 19    min(case when event = 'a' then time end) as a,
 20    min(case when event = 'b' then time end) as b,
 21    min(case when event = 'c' then time end) as c
 22  from test
 23  group by id
 24  order by id;

        ID A     B     C
---------- ----- ----- -----
         1 13:00 14:00 15:00
         2 11:30 15:04 14:15
         3 12:30 16:00

SQL>

CodePudding user response:

Use PIVOT without the ROW_NUMBER filter:

SELECT *
FROM   table_name
PIVOT (
  MIN(time)
  FOR event IN ('a' AS a, 'b' AS b, 'c' AS c)
);

Which, for the sample data:

CREATE TABLE table_name (ID, Event, Time) AS
  SELECT 1, 'a', INTERVAL '13:00:00' HOUR TO SECOND FROM DUAL UNION ALL
  SELECT 1, 'a', INTERVAL '13:05:00' HOUR TO SECOND FROM DUAL UNION ALL
  SELECT 1, 'b', INTERVAL '14:00:00' HOUR TO SECOND FROM DUAL UNION ALL
  SELECT 1, 'c', INTERVAL '15:00:00' HOUR TO SECOND FROM DUAL UNION ALL
  SELECT 1, 'd', INTERVAL '16:00:00' HOUR TO SECOND FROM DUAL UNION ALL
  SELECT 1, 'd', INTERVAL '16:02:00' HOUR TO SECOND FROM DUAL UNION ALL
  SELECT 2, 'a', INTERVAL '11:30:00' HOUR TO SECOND FROM DUAL UNION ALL
  SELECT 2, 'b', INTERVAL '15:04:00' HOUR TO SECOND FROM DUAL UNION ALL
  SELECT 2, 'b', INTERVAL '15:06:00' HOUR TO SECOND FROM DUAL UNION ALL
  SELECT 2, 'c', INTERVAL '14:15:00' HOUR TO SECOND FROM DUAL UNION ALL
  SELECT 2, 'c', INTERVAL '17:20:00' HOUR TO SECOND FROM DUAL UNION ALL
  SELECT 3, 'a', INTERVAL '12:30:00' HOUR TO SECOND FROM DUAL UNION ALL
  SELECT 3, 'b', INTERVAL '16:00:00' HOUR TO SECOND FROM DUAL UNION ALL
  SELECT 3, 'c', INTERVAL '13:00:00' HOUR TO SECOND FROM DUAL UNION ALL
  SELECT 3, 'c', INTERVAL '22:00:00' HOUR TO SECOND FROM DUAL;

Outputs:

ID A B C
1 00 13:00:00.000000 00 14:00:00.000000 00 15:00:00.000000
2 00 11:30:00.000000 00 15:04:00.000000 00 14:15:00.000000
3 00 12:30:00.000000 00 16:00:00.000000 00 13:00:00.000000

fiddle

  • Related