Use SUM function in oracle - sql

I have a table in Oracle which contains :
id | month | payment | rev
----------------------------
A | 1 | 10 | 0
A | 2 | 20 | 0
A | 2 | 30 | 1
A | 3 | 40 | 0
A | 4 | 50 | 0
A | 4 | 60 | 1
A | 4 | 70 | 2
I want to calculate the payment column (SUM(payment)). For (id=A month=2) and (id=A month=4), I just want to take the greatest value from REV column. So that the sum is (10+30+40+70)=150. How to do it?

You can also use below.
select id,sum(payment) as value
from
(
select id,month,max(payment) from table1
group by id,month
)
group by id
Edit: for checking greatest rev value
select id,sum(payment) as value
from (
select id,month,rev,payment ,row_number() over (partition by id,month order by rev desc) as rno from table1
) where rno=1
group by id

This presupposes you don't have more than one value per rev. If that's not the case, then you probably want a row_number analytic instead of max.
with latest as (
select
id, month, payment, rev,
max (rev) over (partition by id, month) as max_rev
from table1
)
select sum (payment)
from latest
where rev = max_rev

Or there's this, if I've understood the requirement right:
with demo as (
select 'A'as id, 1 as month, 10 as payment, 0 as rev from dual
union all select 'A',2,20,0 from dual
union all select 'A',2,30,1 from dual
union all select 'A',3,40,0 from dual
union all select 'A',4,50,0 from dual
union all select 'A',4,60,1 from dual
union all select 'A',4,70,2 from dual
)
select sum(payment) keep (dense_rank last order by rev)
from demo;
You can check the breakdown by including the key columns:
with demo as (
select 'A'as id, 1 as month, 10 as payment, 0 as rev from dual
union all select 'A',2,20,0 from dual
union all select 'A',2,30,1 from dual
union all select 'A',3,40,0 from dual
union all select 'A',4,50,0 from dual
union all select 'A',4,60,1 from dual
union all select 'A',4,70,2 from dual
)
select id, month, max(rev)
, sum(payment) keep (dense_rank last order by rev)
from demo
group by id, month;

select sum(payment) from tableName where id='A' and month=2 OR month=4 order by payment asc;

Related

In Oracle, how to select specific row while aggregating all rows

I have a requirement that I need to both aggregate all rows by id, and find 1 specific row among the rows of the same id. It's like 2 SQL queries, but I want to make it in 1 SQL query. I'm using Oracle database.
for example,table t1 whose data looks like:
id | name | num
----- -------- -------
1 | 'a' | 1
2 | 'b' | 3
2 | 'c' | 6
2 | 'd' | 6
I want to aggregate the data by the id, find the 'name' with the highest 'count', and sum all count of the id to 'total_count'.
There are 2 rows with same num, pick up the first one.
id | highest_num | name_of_highest_num | total_num | avg_num
----- ------------- --------------------- ------------ -------------------
1 | 1 | 'a' | 1 | 1
2 | 6 | 'c' | 15 | 5
Can I get this result by 1 Oracle SQL query?
Thanks in advance for any replies.
Oracle Setup:
CREATE TABLE table_name ( id, name, num ) AS
SELECT 1, 'a', 1 FROM DUAL UNION ALL
SELECT 2, 'b', 3 FROM DUAL UNION ALL
SELECT 2, 'c', 6 FROM DUAL UNION ALL
SELECT 2, 'd', 6 FROM DUAL;
Query:
SELECT id,
MAX( num ) AS highest_num,
MAX( name ) KEEP ( DENSE_RANK LAST ORDER BY num ) AS name_of_highest_num,
SUM( num ) AS total_num,
AVG( num ) AS avg_num
FROM table_name
GROUP BY id
Output:
ID HIGHEST_NUM NAME_OF_HIGHEST_NUM TOTAL_NUM AVG_NUM
-- ----------- ------------------- --------- -------
1 1 a 1 1
2 6 d 15 5
Here's one option using row_number in a subquery with conditional aggregation:
select id,
max(num) as highest_num,
max(case when rn = 1 then name end) as name_of_highest_num,
sum(num) as total_num,
avg(num) as avg_num
from (
select id, name, num,
row_number() over (partition by id order by num desc) rn
from a
) t
group by id
SQL Fiddle Demo
Sounds like you want to use some analytic functions. Something like this should work
select id,
num highest_num,
name name_of_highest_num,
total total_num,
average avg_num
from (select id,
num,
name,
rank() over (partition by id
order by num desc, name asc) rnk,
sum(num) over (partition by id) total,
avg(num) over (partition by id) average
from table t1)
where rnk = 1

select rows between two character values of a column

I have a table which shows as below:
S.No | Action
1 | New
2 | Dependent
3 | Dependent
4 | Dependent
5 | New
6 | Dependent
7 | Dependent
8 | New
9 | Dependent
10 | Dependent
I here want to select the rows between the first two 'New' values in the Action column, including the first row with the 'New' action. Like [New,New)
For example:
In this case, I want to select rows 1,2,3,4.
Please let me know how to do this.
Hmmm. Let's count up the cumulative number of times that New appears as a value and use that:
select t.*
from (select t.*,
sum(case when action = 'New' then 1 else 0 end) over (order by s_no) as cume_new
from t
) t
where cume_new = 1;
you can do some magic with analytic functions
1 select group of NEW actions, to get min and max s_no
2 select lead of 2 rows
3 select get between 2 sno (min and max)
with t as (
select 1 sno, 'New' action from dual union
select 2,'Dependent' from dual union
select 3,'Dependent' from dual union
select 4,'Dependent' from dual union
select 5,'New' from dual union
select 6,'Dependent' from dual union
select 7,'Dependent' from dual union
select 8,'New' from dual union
select 9,'Dependent' from dual union
select 10,'Dependent' from dual
)
select *
from (select *
from (select sno, lead(sno) over (order by sno) a
from ( select row_number() over (partition by action order by Sno) t,
t.sno
from t
where t.action = 'New'
) a
where t <=2 )
where a is not null) a, t
where t.sno >= a.sno and t.sno < a.a

Oracle - count records if number of token less than 2

I have records like...
ID | KEY
-------|---------
1 | 123_456_abc
1 | 123_xyz
1 | 456_abc
2 | 123_abc
2 | 122_73_zcc
3 | 123_wer
4 | 345_23_fhd
4 | 3453_abc
5 | ad1fr2h3_abcasd
5 | ers2g45bb_abc2rtd
5 | asf23g_abc1_sf45
I want count(ID) where count(tokanize(numeric(KEY),'_')) < 2
As count(ID) will be 6
You can try something like this
SELECT COUNT(ID) FROM xyz WHERE key NOT LIKE '%_%_%';
This should filter all elements which have less than two underscores.
Try this :
select Count(1) from
(with abc(id,key) as (select '1','123_456_abc' from dual
Union all
select '1','123_xyz' from dual
UNion all
select '1','456_abc' from dual
Union all
select '2','123_abc' from dual
UNion all
select '2','123_73_zcc' from dual
Union all
select '3','123_wer' from dual
UNion all
select '1','345_23_fhd' from dual
UNion all
select '1','345_abc' from dual
)
select key, length(regexp_replace(key,'[^_]*','')) cntr
from abc )
where cntr = 1
eliminate all records which has more than 1 underscores
then eliminate the ones which do not start with a number
then sum it up
select sum(cnt) from (
select key, cnt, id from (
select key, length(regexp_replace(key,'[^_]*','')) cnt, id from table_name
) where cnt < 2
) where regexp_like(key,'[1-9]+(.)*')

Select Duplicate records in Oracle

I have a table below in Oracle
Table1
State | Product |other fields
CA | P1 | xxxx
OR | P1 | xxxx
OR | P1 | xxxx
OR | P1 | xxxx
WA | P1 | xxxx
VA | P2 | xxxx
My Output should be only select if State has been occurred more than once.
State | Product |other fields
OR | P1 | xxxx
If you just want to count duplicate states then:
SELECT DISTINCT
State,
Product,
Other_Fields
FROM (
SELECT t.*,
COUNT(1) OVER ( PARTITION BY State ) AS cnt
FROM Table1 t
)
WHERE cnt > 1;
If you want to consider duplicate rows (considering all fields) then:
SELECT *
FROM Table1
GROUP BY State, Product, Other_Fields
HAVING COUNT(1) > 1;
select state, product, column_3, column_4
from (
select state, product, column_3, column_4,
count(*) over (partition by state) as cnt
from the_table
) t
where cnt > 1;
You could use ROW_NUMBER analytic function.
For example,
SQL> WITH sample_data AS(
2 SELECT 'CA' State, 'P1' product FROM dual UNION ALL
3 SELECT 'OR', 'P1' product from dual union all
4 SELECT 'OR', 'P1' product FROM dual UNION ALL
5 SELECT 'OR', 'P1' product FROM dual UNION ALL
6 SELECT 'WA', 'P1' product FROM dual UNION ALL
7 SELECT 'VA', 'P2' product from dual
8 )
9 -- end of sample_data mimicking real table
10 SELECT distinct state,
11 product
12 FROM
13 (SELECT state,
14 product,
15 row_number() OVER(PARTITION BY state ORDER BY product) rn
16 FROM sample_data
17 )
18 WHERE rn >1;
ST PR
-- --
OR P1
SQL>

How to group data according the condition and sequentially to number these groups?

I have next data:
with t as
(select 1 as id, '1324345' as amount, 7821 as code
from dual
union all
select 2 as id, 'current' as amount, 2210 as code
from dual
union all
select 3 as id, 'link' as amount, 2210 as code
from dual
union all
select 4 as id, '56236400' as amount, 6740 as code
from dual
union all
select 5 as id, '45562330' as amount, 5578 as code
from dual
union all
select 6 as id, '34875930' as amount, 5828 as code
from dual
union all
select 7 as id, 'current' as amount, 8520 as code
from dual
union all
select 8 as id, 'link' as amount, 8520 as code
from dual
union all
select 9 as id, '6731347060' as amount, 4740 as code
from dual
union all
select 10 as id, '346008600' as amount, 6575 as code
from dual)
select * from t
and I want to get the following:
with t as
(select 1 as id, '1324345' as amount, 7821 as code, 1 as group_id
from dual
union all
select 2 as id, 'current' as amount, 2210 as code, 2 as group_id
from dual
union all
select 3 as id, 'link' as amount, 2210 as code, 2 as group_id
from dual
union all
select 4 as id, '56236400' as amount, 6740 as code, 3 as group_id
from dual
union all
select 5 as id, '45562330' as amount, 5578 as code, 3 as group_id
from dual
union all
select 6 as id, '34875930' as amount, 5828 as code, 3 as group_id
from dual
union all
select 7 as id, 'current' as amount, 8520 as code, 4 as group_id
from dual
union all
select 8 as id, 'link' as amount, 8520 as code, 4 as group_id
from dual
union all
select 9 as id, '6731347060' as amount, 4740 as code, 5 as group_id
from dual
union all
select 10 as id, '346008600' as amount, 6575 as code, 5 as group_id
from dual)
select * from t
The condition is the value of "amount" field. It may be number or text.
UPD: Expected result:
id | amount | code | group_id
---------------------------------------------
1 | 1324345 | 7821 | 1
---------------------------------------------
2 | current | 2210 | 2
---------------------------------------------
3 | link | 2210 | 2
---------------------------------------------
4 | 56236400 | 6740 | 3
---------------------------------------------
5 | 45562330 | 5578 | 3
---------------------------------------------
6 | 34875930 | 5828 | 3
---------------------------------------------
7 | current | 8520 | 4
---------------------------------------------
8 | link | 8520 | 4
---------------------------------------------
9 | 6731347060 | 4740 | 5
---------------------------------------------
10 | 346008600 | 6575 | 5
---------------------------------------------
EDIT: best solution:
with tmain as
(select t.*,
decode(isnumeric(Amount),
lag(isnumeric(Amount)) over(order by id),
null,
1) lg
from t
order by id)
select id, amount, code, count(lg) over(order by id) group_id from tmain
Where isnumeric function is (based on #valexhome answer):
CREATE OR REPLACE FUNCTION ISNUMERIC (Str IN CHAR) RETURN NUMBER AS
TMP int;
BEGIN
if Str is null then
return(null);
end if;
TMP:=TO_NUMBER(Str);
RETURN (1);
EXCEPTION
WHEN OTHERS THEN
RETURN (0);
END;
Here is function ISNUMERIC to define before a query run:
CREATE OR REPLACE FUNCTION ISNUMERIC (Str IN CHAR) RETURN NUMBER AS
TMP int;
BEGIN
if Str is null then
return(null);
end if;
--if input null return NULL
TMP:=TO_NUMBER(Str);
RETURN (1);
EXCEPTION
WHEN OTHERS THEN
RETURN (0);
END;
And here is the query:
select id, amount, code,
(
select count(id)
from t Tab
where tab.id<=t.id
and
isnumeric(Amount)<>nvl(isnumeric((select Amount from t d1 where d1.id=(select max(d.id) from t d where (d.id<Tab.id)))),isnumeric(Amount)-1)
) Group_id
from t order by id
I found another solution:
with tmain as
(select t.*,
decode(isnumeric(Amount),
lag(isnumeric(Amount)) over(order by id),
null,
1) lg
from t
order by id)
select id, amount, code, count(lg) over(order by id) group_id from tmain
It works fine on large amounts of data.