简体   繁体   中英

oracle sql select/set rownumber limitation differently for different group in one data set

I have a table have 4 columns: customer, product_id, score, tag there are 2 value in tag column 'new' and 'old'

for each customer has both product_id from 'new' and 'old' tag(number varies) for each customer, we rank product_id based on score and tag we have total limitation for the number of product for each customer. called it 'n_prod'

I hope to select (2/3 *n_prod) product_id from 'old' tag and (1/3*n_prod) product for 'new' tag for each customer eg if we need to choose 6 product, hope having 4 from 'old' tag (top 4 based on the score), and have 2 from 'new' tag (the top 2 based on score)

I am able to create a column called 'rn' use the following command to rank product_id based on each customer and tag

select customer, product_id, score, tag
      , row_number()over(partition by customer,tag, order by score desc) as rn 
from table

the number of limitation is different for different group: tried this but does not work:

with tep as
(select customer, product_id, score, tag
      , row_number()over(partition by customer,tag, order by score desc) as rn 
from table)

select tep.*
from tep
where ( case 
         when tag='new' then rn<= round(n_prod*0.33,0)
         else then rn<= round(n_prod*0.66,0)
         end
);

could I combine 'where' with 'case when ' or 'if else'?

restate the expected result: I hope to select (2/3 *n_prod) product_id from 'old' tag and (1/3*n_prod) product for 'new' tag for each customer eg if we need to choose 6 product, hope having 4 from 'old' tag (top 4 based on the score), and have 2 from 'new' tag (the top 2 based on score)

It's hard to be sure without data, but I think you just need to use Boolean logic in your where clause:

...
select tep.*
from tep
where (tag = 'new' and rn <= round(n_prod*0.33))
   or (tag = 'old' and rn <= round(n_prod*0.66));

Quick demo with some made-up data in another CTE, and n_prod as a bind variable:

var n_prod number;
exec :n_prod := 6;

with your_table (customer, product_id, score, tag) as (
            select 1234, 2345, level, 'old' from dual connect by level <= 10
  union all select 1234, 2345, level, 'new' from dual connect by level <= 10
),
tep as
(select customer, product_id, score, tag
      , row_number()over(partition by customer,tag order by score desc) as rn 
from your_table)
select tep.*
from tep
where (tag = 'new' and rn <= round(:n_prod*0.33))
or (tag = 'old' and rn <= round(:n_prod*0.66));

  CUSTOMER PRODUCT_ID      SCORE TAG         RN
---------- ---------- ---------- --- ----------
      1234       2345         10 new          1
      1234       2345          9 new          2
      1234       2345         10 old          1
      1234       2345          9 old          2
      1234       2345          8 old          3
      1234       2345          7 old          4

Incidentally, you probably need to increase the number of digits in the multipliers you're using for large values of n_prod . With 0.33 and 0.66 the total number of rows returned starts to go wrong after... er... 28. ( round(28*.33) is 9; round(28*.66) is 18; so the total is 27 instead of 28. Using 0.333 and 0.666 seems to be safe until 253; using 0.3333 and 0.6666 until 2503; etc.

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