user123
user123

Reputation: 425

oracle - group by - no aggregation

I have an oracle table , where ref_id is the flag field is the type of data and ORN is the order of data in each ref_id :

ref_id   data    ORN   flag
  1       100     0     0
  1       200     1     0
  1       300     2     0
  1       400     3     0
  1       110     0     1
  1       210     1     1
  1       150     0     2
  1       250     1     2
  1       350     2     2
  1       450     3     2
  2       500     0     0
  2       600     1     0
  2       700     2     0
  2       800     3     0
  2       120     0     1
  2       220     1     1
  2       320     1     1
  2       420     1     1
  2       170     0     2
  2       270     1     2
  2       370     2     2
  2       470     3     2

I need to group the data in a way to get last data in flag 0 and last data in flag 2 for each ref_id

so the new table will be something like this:

ref_id    data_1    data_2
  1        400       450
  2        800       470

any hint how to accomplish this without using loops?

Upvotes: 0

Views: 105

Answers (3)

Slkrasnodar
Slkrasnodar

Reputation: 824

You may use the aggregate functions (FIRST/LAST) for the purpose.

https://docs.oracle.com/database/121/SQLRF/functions074.htm#SQLRF00641

https://docs.oracle.com/database/121/SQLRF/functions095.htm#SQLRF00653.

with t (ref_id,data,ORN,flag) as (
select   1,       100,     0,     0 from dual union all
select   1,       200,     1,     0 from dual union all
select   1,       300,     2,     0 from dual union all
select   1,       400,     3,     0 from dual union all
select   1,       110,     0,     1 from dual union all
select   1,       210,     1,     1 from dual union all
select   1,       150,     0,     2 from dual union all
select   1,       250,     1,     2 from dual union all
select   1,       350,     2,     2 from dual union all
select   1,       450,     3,     2 from dual union all

select   2,       500,     0,     0 from dual union all
select   2,       600,     1,     0 from dual union all
select   2,       700,     2,     0 from dual union all
select   2,       800,     3,     0 from dual union all
select   2,       120,     0,     1 from dual union all
select   2,       220,     1,     1 from dual union all
select   2,       320,     1,     1 from dual union all
select   2,       420,     1,     1 from dual union all
select   2,       170,     0,     2 from dual union all
select   2,       270,     1,     2 from dual union all
select   2,       370,     2,     2 from dual union all
select   2,       470,     3,     2 from dual 
)
select 
  ref_id
, max(decode(flag, 0, data)) keep (dense_rank last order by decode(flag, 0, 100, 50), orn ) x
, max(decode(flag, 2, data)) keep (dense_rank last order by decode(flag, 2, 100, 50), orn ) y 
-- or
, min(decode(flag, 0, data)) keep (dense_rank first order by decode(flag, 0, 50, 100), orn desc) xx
, min(decode(flag, 2, data)) keep (dense_rank first order by decode(flag, 2, 50, 100), orn desc) yy

from t
group by ref_id

    REF_ID          X          Y         XX         YY
---------- ---------- ---------- ---------- ----------
         1        400        450        400        450
         2        800        470        800        470

Upvotes: 0

Marmite Bomber
Marmite Bomber

Reputation: 21063

Alternatively use a two step approach, first (in the CTE) select only the values of the DATA column that corresponds to the last ORN within the REF_ID

Note that is case the ORNis not unique you may get more than one row potentially with different values.

In the next step simple aggregate on REF_ID, I'm using max function, i.e. this will get that highest value of DATA in case of ties.

In case the combination of REF_ID and ORN is unique (primary key) you may use MIN and MAX interchangeable, but it is good to know that they will provide diffremt result if dups are allowed.

with agg as (
select 
 REF_ID,FLAG, DATA, ORN,
 case when flag = 0  and ORN = max(ORN) over (partition by REF_ID, FLAG) then data end as data_0,
 case when flag = 2  and ORN = max(ORN) over (partition by REF_ID, FLAG) then data end as data_2
from tab
)
select  REF_ID, 
 max(data_0) as data_0,
 max(data_2) as data_2
from agg
group by REF_ID
order by 1;

Here the result of the CTE

    REF_ID       FLAG       DATA        ORN     DATA_0     DATA_2
---------- ---------- ---------- ---------- ---------- ----------
         1          0        100          0                      
         1          0        200          1                      
         1          0        300          2                      
         1          0        400          3        400           
         1          1        110          0                      
         1          1        210          1                      
         1          2        150          0                      
         1          2        250          1                      
         1          2        350          2                      
         1          2        450          3                   450
...

and the result of the final query

    REF_ID     DATA_0     DATA_2
---------- ---------- ----------
         1        400        450
         2        800        470   

Upvotes: 0

Popeye
Popeye

Reputation: 35900

You can use the analytical function and group by as follows:

SELECT REF_ID, 
       MAX(CASE WHEN FLAG = 0 THEN DATA END) AS DATA_0,
       MAX(CASE WHEN FLAG = 2 THEN DATA END) AS DATA_2
FROM
(
  SELECT REF_ID, DATA, ORN, FLAG,
         ROW_NUMBER() OVER (PARTITION BY REF_ID, FLAG ORDER BY ORN DESC) AS RN
  FROM YOUR_TABLE 
  WHERE FLAG IN (0,2)
)
WHERE RN = 1
GROUP BY REF_ID

Upvotes: 1

Related Questions