Oracle: How to restart row_number function - oracle

I have a scenario where in I need to assign row_number for a data grouped by department, branch, amount and ordering by date.
If the amount repeats after some dates for the same department & branch, I would like to have the row_number as 1.
Could you please let me know how to achieve this?
When I tried with Row_number function the numbering is continuing after some dates.
Sample Data:
This is the row numbers I am getting if the following function is used row_number() over(Partition by Department, Branch, Amount order by Date)
Department Branch Amount Date Row_number()
Dep A Bran 1 51 25-Oct-12 1
Dep A Bran 1 45.5 26-Nov-12 1
Dep A Bran 1 45.5 05-Apr-13 2
Dep A Bran 1 45.5 06-May-13 3
Dep A Bran 1 65 07-May-13 1
Dep A Bran 1 51 26-Aug-13 2
Dep A Bran 1 51 11-Sep-13 3
But I am expecting output in the below order.
Department Branch Amount Date Row_number()
Dep A Bran 1 51 25-Oct-12 1
Dep A Bran 1 45.5 26-Nov-12 1
Dep A Bran 1 45.5 05-Apr-13 2
Dep A Bran 1 45.5 06-May-13 3
Dep A Bran 1 65 07-May-13 1
Dep A Bran 1 51 26-Aug-13 1
Dep A Bran 1 51 11-Sep-13 2
Could anyone help me on this?

You need to change how you identify the groups inside which you calculate row_number().
Something like:
SQL> with t (Department, Branch, Amount, Date#)
2 as (
3 select 'Dep A', 'Bran 1', 51, to_date('25-10-2012','DD-MM-YYYY') from dual union all
4 select 'Dep A', 'Bran 1', 45.5, to_date('26-11-2012','DD-MM-YYYY') from dual union all
5 select 'Dep A', 'Bran 1', 45.5, to_date('05-04-2013','DD-MM-YYYY') from dual union all
6 select 'Dep A', 'Bran 1', 45.5, to_date('06-05-2013','DD-MM-YYYY') from dual union all
7 select 'Dep A', 'Bran 1', 65, to_date('07-05-2013','DD-MM-YYYY') from dual union all
8 select 'Dep A', 'Bran 1', 51, to_date('26-08-2013','DD-MM-YYYY') from dual union all
9 select 'Dep A', 'Bran 1', 51, to_date('11-09-2013','DD-MM-YYYY') from dual
10 )
11 select department, branch, amount, date#, row_number() over(partition by grp order by date#) rn
12 from (
13 select department, branch, amount, date#, sum(st_grp) over(order by date#) grp from (
14 select department, branch, amount, date#, case when amount = lag(amount,1,amount) over(order by date#) then 0 else 1 end st_grp from t
15 )
16 )
17 order by date#
18 /
DEPAR BRANCH AMOUNT DATE# RN
----- ------ ---------- -------- ----------
Dep A Bran 1 51 25.10.12 1
Dep A Bran 1 45,5 26.11.12 1
Dep A Bran 1 45,5 05.04.13 2
Dep A Bran 1 45,5 06.05.13 3
Dep A Bran 1 65 07.05.13 1
Dep A Bran 1 51 26.08.13 1
Dep A Bran 1 51 11.09.13 2

Related

Need output in below format in oracle. I have tried with lag function it worked if we have only 2 rows in table

Consider below table table.
Id balance
1 100
2 500
3 4000
I need output in below format.
Id balance begin_bal end_bal
1 100 0 100
2 500 100 600
3 4000 600 4600
A little bit of analytics, as you presumed:
SQL> with test (id, balance) as
2 (select 1, 100 from dual union all
3 select 2, 500 from dual union all
4 select 3, 4000 from dual
5 ),
6 temp as
7 (select id, balance, sum(balance) over (order by id) rsum
8 from test
9 )
10 select id,
11 balance,
12 nvl(lag(rsum) over (order by id), 0) begin_bal,
13 rsum end_bal
14 from temp
15 order by id;
ID BALANCE BEGIN_BAL END_BAL
---------- ---------- ---------- ----------
1 100 0 100
2 500 100 600
3 4000 600 4600
SQL>

Want to convert rows to columns

I have one table t as below
Customer _no | receipt_no
----------------------------
A | 123
A. | 234
A. | 345
B. | 465
B. | 675
I want result as
Customer _no | receipt_1 | receipt_2
A. | 123. | 234
A. | 345. | Null
B. | 465. | 675
Please suggest how to do this
If I understood it correctly, you want to have two columns for each customer. If that's so, here's one option.
SQL> with test (cno, rno) as
2 (select 'A', 123 from dual union all
3 select 'A', 234 from dual union all
4 select 'A', 345 from dual union all
5 select 'A', 444 from dual union all
6 select 'A', 555 from dual union all
7 select 'B', 456 from dual union all
8 select 'B', 675 from dual
9 ),
10 inter as
11 (select cno, rno,
12 row_number() over (partition by cno order by cno, rno) rn,
13 round(row_number() over (partition by cno order by cno, rno) /2) grp
14 from test
15 )
16 select cno,
17 max(decode(mod(rn, 2), 1, rno)) r1,
18 max(decode(mod(rn, 2), 0, rno)) r2
19 from inter
20 group by cno, grp
21 order by cno, grp;
C R1 R2
- ---------- ----------
A 123 234
A 345 444
A 555
B 456 675
SQL>

Select Top (Max) Amount From Two Of Four Fields (Columns)

I have this query
SELECT code, username, week1money, week2money, week3money, week4money FROM(
--subquery goes here
)
How to select the top two weeks, i.e. weeks with the highest value? I want to sum the top two weeks to be precise.
If I understand correct you want to get 2 top values per every (code, username) row and (code, username) is a key of recordset.
Supposing you can have two top weeks with the same values and you don't have nulls this might be one of solutions:
SQL> with t (id, code, week1, week2, week3, week4)
2 as (
3 select 1, 'a', 10, 15, 11, 8 from dual union all
4 select 2, 'b', 7, 4, 2, 9 from dual union all
5 select 3, 'c', 3, 3, 1, 0 from dual
6 )
7 select id, code, max(week) first_top, min(week) next_top from (
8 select id, code, row_number() over(partition by id, code order by week desc) rnk, week
9 from (
10 select t.id, t.code,
11 decode(r.rn,1,week1,2,week2,3,week3,4,week4) week
12 from t,
13 (select rownum rn from dual connect by level <= 4) r
14 ))
15 where rnk in (1,2)
16 group by id, code
17 /
ID C FIRST_TOP NEXT_TOP
---------- - ---------- ----------
3 c 3 3
1 a 15 11
2 b 9 7
If you have non-null and different values in weeks you can use something like:
SQL> with t (id, code, week1, week2, week3, week4)
2 as (
3 select 1, 'a', 10, 15, 11, 8 from dual union all
4 select 2, 'b', 7, 4, 2, 9 from dual union all
5 select 3, 'c', 3, 2, 1, 0 from dual
6 )
7 select id, code
8 , greatest(week1, week2, week3, week4) first_top
9 , greatest(
10 case when week1 < greatest(week1, week2, week3, week4) then week1 else -1e28 end,
11 case when week2 < greatest(week1, week2, week3, week4) then week2 else -1e28 end,
12 case when week3 < greatest(week1, week2, week3, week4) then week3 else -1e28 end,
13 case when week4 < greatest(week1, week2, week3, week4) then week4 else -1e28 end
14 ) second_top
15 from t
16 /
ID C FIRST_TOP SECOND_TOP
---------- - ---------- ----------
1 a 15 11
2 b 9 7
3 c 3 2
But to get the right solution more details are required.
Answering my question...
select * from(
select * from(
select week1money col from dual
union
select week2money col from dual
union
select week3money col from dual
union
select week4money col from dual
) order by col desc
) where rownum < 3
Using GREATESTS() also may help.

Ranking with Duplicates

I am using Oracle Apex and I am ranking call speed. I am almost getting the results I am after. The only issue that I have faced is that when the rank function comes across duplicate values, by default they both get assigned the lowest Rank. e.g.
Rank Call Speed
1 65
2 72
3 92
4 102
4 102
4 102
4 102
4 102
9 113
10 154
11 201
12 352
Is there anyway to have the 4's represent as 8's (the highest rank of the duplicates)
One way of doing this is by using the ranking Descending and then subtracting this from the highest rank + 1. this works but seems like an unnecessary step.
Any help will be much appreciated
Kind of a strange thing to do, but I would do something like:
with data as (
select 65 call_speed from dual union all
select 72 call_speed from dual union all
select 92 call_speed from dual union all
select 102 call_speed from dual connect by level <= 5 union all
select 113 call_speed from dual union all
select 154 call_speed from dual union all
select 201 call_speed from dual union all
select 352 call_speed from dual
)
select
rank() over (order by call_speed) + count(*) over (partition by call_speed) - 1 rank,
call_speed
from data;
Which gives you:
RANK CALL_SPEED
---------- ----------
1 65
2 72
3 92
8 102
8 102
8 102
8 102
8 102
9 113
10 154
11 201
12 352
Just an alternative, for no reason at all, except maybe to avoid any memory overhead (?) from doing a partitioned count:
with data as (
select 65 call_speed from dual union all
select 72 call_speed from dual union all
select 92 call_speed from dual union all
select 102 call_speed from dual connect by level <= 5 union all
select 113 call_speed from dual union all
select 154 call_speed from dual union all
select 201 call_speed from dual union all
select 352 call_speed from dual
)
select
count(*) over () + 1 - rank() over (order by call_speed desc) rank,
call_speed
from data
order by call_speed;

Oracle Calculation Involving Results of Another Calculation

First off, I'm a total Oracle noob although I'm very familiar with SQL. I have a single cost column. I need to calculate the total cost, the percentage of the total cost, and then a running sum of the percentages. I'm having trouble with the running sum of percentages because the only way I can think to do this uses nested SUM functions, which isn't allowed.
Here's what works:
SELECT cost, SUM(cost) OVER() AS total, cost / SUM(cost) OVER() AS per
FROM my_table
ORDER BY cost DESC
Here's what I'm trying to do that doesn't work:
SELECT cost, SUM(cost) OVER() AS total, cost / SUM(cost) OVER() AS per,
SUM(cost/SUM(cost) OVER()) OVER(cost) AS per_sum
FROM my_table
ORDER BY cost DESC
Am I just going about it wrong, or is what I'm trying to do just not possible? By the way I'm using Oracle 10g. Thanks in advance for any help.
You don't need the order by inside that inline view, especially since the outer select is doing an order by the order way around. Also, cost / SUM(cost) OVER () equals RATIO_TO_REPORT(cost) OVER ().
An example:
SQL> create table my_table(cost)
2 as
3 select 10 from dual union all
4 select 20 from dual union all
5 select 5 from dual union all
6 select 50 from dual union all
7 select 60 from dual union all
8 select 40 from dual union all
9 select 15 from dual
10 /
Table created.
Your initial query:
SQL> SELECT cost, SUM(cost) OVER() AS total, cost / SUM(cost) OVER() AS per
2 FROM my_table
3 ORDER BY cost DESC
4 /
COST TOTAL PER
---------- ---------- ----------
60 200 .3
50 200 .25
40 200 .2
20 200 .1
15 200 .075
10 200 .05
5 200 .025
7 rows selected.
Quassnoi's query contains a typo:
SQL> SELECT cost, total, per, SUM(running) OVER (ORDER BY cost)
2 FROM (
3 SELECT cost, SUM(cost) OVER() AS total, cost / SUM(cost) OVER() AS per
4 FROM my_table
5 ORDER BY
6 cost DESC
7 )
8 /
SELECT cost, total, per, SUM(running) OVER (ORDER BY cost)
*
ERROR at line 1:
ORA-00904: "RUNNING": invalid identifier
And if I correct that typo. It gives the right results, but wrongly sorted (I guess):
SQL> SELECT cost, total, per, SUM(per) OVER (ORDER BY cost)
2 FROM (
3 SELECT cost, SUM(cost) OVER() AS total, cost / SUM(cost) OVER() AS per
4 FROM my_table
5 ORDER BY
6 cost DESC
7 )
8 /
COST TOTAL PER SUM(PER)OVER(ORDERBYCOST)
---------- ---------- ---------- -------------------------
5 200 .025 .025
10 200 .05 .075
15 200 .075 .15
20 200 .1 .25
40 200 .2 .45
50 200 .25 .7
60 200 .3 1
7 rows selected.
I think this is the one you are looking for:
SQL> select cost
2 , total
3 , per
4 , sum(per) over (order by cost desc)
5 from ( select cost
6 , sum(cost) over () total
7 , ratio_to_report(cost) over () per
8 from my_table
9 )
10 order by cost desc
11 /
COST TOTAL PER SUM(PER)OVER(ORDERBYCOSTDESC)
---------- ---------- ---------- -----------------------------
60 200 .3 .3
50 200 .25 .55
40 200 .2 .75
20 200 .1 .85
15 200 .075 .925
10 200 .05 .975
5 200 .025 1
7 rows selected.
Regards,
Rob.
SELECT cost, total, per, SUM(per) OVER (ORDER BY cost)
FROM (
SELECT cost, SUM(cost) OVER() AS total, cost / SUM(cost) OVER() AS per
FROM my_table
)
ORDER BY
cost DESC

Resources