ORACLE SQL, I don't know how to use SUM() here - sql

Table TRANSACTION:
TRANS_VALUE
USER ID
TRANS_TYPE_ID
10
1
2
5
2
1
15
1
1
20
2
2
10
1
2
5
1
2
15
3
1
20
3
1
I need to get to this:
USER
SUM(TRANS_TYPE_1)
SUM(TRANS_TYPE_2)
1
15
25
2
5
20
3
35
NULL
Can someone help me?
I tried this but sadness
SELECT
user_id AS "USER
SUM(trans_value)
FROM
TRANSACTION
WHERE
trans_value = 1
GROUP BY
user_id
ORDER BY 1;
I need to get to this
USER
SUM(TRANS_TYPE_1)
SUM(TRANS_TYPE_2)
1
15
25
2
5
20
3
35
NULL

Use conditional aggregation:
SELECT user_id,
SUM(CASE trans_type_id WHEN 1 THEN trans_value END) AS sum_trans_type_1,
SUM(CASE trans_type_id WHEN 2 THEN trans_value END) AS sum_trans_type_2
FROM transaction
GROUP BY user_id
or PIVOT:
SELECT *
FROM transaction
PIVOT (
SUM(trans_value)
FOR trans_type_id IN (
1 AS sum_trans_type_1,
2 AS sum_trans_type_2
)
)
Which, for the sample data:
CREATE TABLE transaction (TRANS_VALUE, USER_ID, TRANS_TYPE_ID) AS
SELECT 10, 1, 2 FROM DUAL UNION ALL
SELECT 5, 2, 1 FROM DUAL UNION ALL
SELECT 15, 1, 1 FROM DUAL UNION ALL
SELECT 20, 2, 2 FROM DUAL UNION ALL
SELECT 10, 1, 2 FROM DUAL UNION ALL
SELECT 5, 1, 2 FROM DUAL UNION ALL
SELECT 15, 3, 1 FROM DUAL UNION ALL
SELECT 20, 3, 1 FROM DUAL;
Both output:
USER_ID
SUM_TRANS_TYPE_1
SUM_TRANS_TYPE_2
1
15
25
2
5
20
3
35
null
fiddle

Related

Check Distinct value Present in the group

I have a table with multiple pos and I need to find the Purchase Id where it has only wallet per group and nothing else in the group.
For eg,here PID - 4 and 5 has only wallet , rest has other's as well. So wallet_flag should be 1 in the output.
I tried to use window's function but could not achieve the result. Can you please suggest.
select PID
,POS
, SUM(CASE WHEN POS='bwallet' THEN 1 ELSE 0 END ) OVER(PARTITION BY PID) as FLAG
from PAYMENTS
where "status" = 'SUCCESS'
OUTPUT:
Here's one option:
Sample data:
SQL> with test (pid, pos, amount) as
2 (select 1, 'wallet', 10 from dual union all
3 select 1, 'BT' , 10 from dual union all
4 select 1, 'Cash' , 10 from dual union all
5 select 2, 'BT' , 50 from dual union all
6 select 3, 'Cash' , 24 from dual union all
7 select 3, 'BT' , 12 from dual union all
8 select 4, 'wallet', 100 from dual union all
9 select 5, 'wallet', 20 from dual union all
10 select 5, 'wallet', 100 from dual
11 ),
Query begins here; cnt will be 0 if there's only "wallet" per PID:
12 temp as
13 (select pid,
14 sum(case when pos = 'wallet' then 0 else 1 end) cnt
15 from test
16 group by pid
17 )
18 select a.pid, a.pos, a.amount,
19 case when b.cnt = 0 then 1 else 0 end wallet_flag
20 from test a join temp b on a.pid = b.pid
21 order by a.pid;
PID POS AMOUNT WALLET_FLAG
---------- ------ ---------- -----------
1 wallet 10 0
1 BT 10 0
1 Cash 10 0
2 BT 50 0
3 Cash 24 0
3 BT 12 0
4 wallet 100 1
5 wallet 20 1
5 wallet 100 1
9 rows selected.
SQL>
SELECT
your_table.*,
MIN(
CASE pos
WHEN 'wallet' THEN 1
ELSE 0
END
)
OVER (
PARTITION BY pid
)
AS wallet_flag
from
your_table
https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=e05a7863b9f4d912dcdf5ced5ec1c1b2

Oracle add sequential query rows if missing

In Oracle 19c I have a data like:
with t as (
select 1 Cat, 1 id, 11 val from dual
union all
select 1, 3, 33 from dual
union all
select 2, 2, 22 from dual
union all
select 2, 4, 44 from dual)
select *
from t
In query result I want to get 4 rows per every cat with ids 1-4 and if there was no such id in that cat a val must be null:
cat
id
val
1
1
11
1
2
1
3
33
1
4
2
1
2
2
22
2
3
2
4
44
Use a PARTITIONed join with a row-generator:
SELECT t.cat,
i.id,
t.val
FROM (SELECT LEVEL AS id FROM DUAL CONNECT BY LEVEL <= 4) i
LEFT OUTER JOIN t
PARTITION BY (t.cat)
ON (i.id = t.id)
Which outputs:
CAT
ID
VAL
1
1
11
1
2
null
1
3
33
1
4
null
2
1
null
2
2
22
2
3
null
2
4
44
db<>fiddle here

Filtering SQL using a oracle database

I would like to know if the following is possible
For example I have a shoe factory. In this factory I have a production line, Every step in this production line is recorded into the oracle database.
if the shoe has completed a production step the result is = 1
example table
Shoe_nr production step result
1 1 1
1 2 1
1 3
2 1 1
2 2 1
2 3
3 1
3 2
3 3
Now the question, is it possible to filter out production step 3 where only the shoes have passed production step 2 which is equal to 1 in result.
I know if it can be done it's probably very easy but if you dont know i found out it's a little bit tricky.
Thanks,
Chris
Yes, you can do it with IN and a Subselect
select *
from shoes
where shoe.id in (
select shoe.id
from shoes
where production_step = 2
and result = 1
)
and production_step = 3
This might be one option; see comments within code (lines #1 - 12 represent sample data; you already have that and don't type it. Query you might be interested in begins at line #13).
SQL> with shoes (shoe_nr, production_step, result) as
2 -- sample data
3 (select 1, 1, 1 from dual union all
4 select 1, 2, 1 from dual union all
5 select 1, 3, null from dual union all
6 select 2, 1, 1 from dual union all
7 select 2, 2, 1 from dual union all
8 select 2, 3, null from dual union all
9 select 3, 1, null from dual union all
10 select 3, 2, null from dual union all
11 select 3, 3, null from dual
12 ),
13 -- which shoes' production step #3 should be skipped?
14 skip as
15 (select shoe_nr
16 from shoes
17 where production_step = 2
18 and result = 1
19 )
20 -- finally:
21 select a.shoe_nr, a.production_step, a.result
22 from shoes a
23 where (a.shoe_nr, a.production_step) not in (select b.shoe_nr, 3
24 from skip b
25 )
26 order by a.shoe_nr, a.production_step;
SHOE_NR PRODUCTION_STEP RESULT
---------- --------------- ----------
1 1 1
1 2 1
2 1 1
2 2 1
3 1
3 2
3 3
7 rows selected.
SQL>
If you just want the shoe_nr that satisfy the condition, you can use aggregation and a having clause:
select shoe_nr
from mytable
group by shoe_nr
having
max(case when production_step = 2 then result end) = 0
and max(case when production_step = 3 then 1 end) = 1
If you want the entire row corresponding to this shoe_nr at step 3, use window functions instead:
select 1
from (
select
t.*,
max(case when production_step = 2 then result end)
over(partition by shoe_nr) as has_completed_step_2
from mytable t
) t
where production_step = 3 and has_completed_step_2 = 0

Oracle - How to use avg(count(*)) on having clause or where clause?

I have data like this:
CONCERT_ID EVENT_ID ATTENDANCE AVG_ATTENDANCE_EACH_CONCERT
---------- ---------- ---------- ---------------------------
1 1 1 1,5
1 2 2 1,5
3 5 2 2
3 6 2 2
5 11 4 2
5 12 1 2
5 13 1 2
Thats from this query:
select concert_id, event_id, count(customer_id) attendance,
avg(count(*)) over (partition by concert_id) avg_attendance_each_concert
from booking
group by concert_id, event_id
order by event_id;
How to make a limitation on that query. What i want to make is
If the attendance is below average attendance show result
I already tried avg(count(*)) over (partition by concert_id) to having clause but gave me an error group function is too deep
It's easy to get the desired results by applying just one nesting :
select * from
(
select concert_id, event_id, count(customer_id) attendance,
avg(count(*)) over (partition by concert_id) avg_attendance_each_concert
from booking
group by concert_id, event_id
order by event_id
)
where attendance < avg_attendance_each_concert
D e M o
Include an "intermediate" table, a query which returned the correct result in your last question. Then select values - which satisfy a new condition - from it.
SQL> with booking (concert_id, event_id, customer_id) as
2 (select 1, 1, 10 from dual union
3 select 1, 2, 10 from dual union
4 select 1, 2, 20 from dual union
5 --
6 select 3, 5, 10 from dual union
7 select 3, 5, 20 from dual union
8 select 3, 6, 30 from dual union
9 select 3, 6, 40 from dual union
10 --
11 select 5, 11, 10 from dual union
12 select 5, 11, 20 from dual union
13 select 5, 11, 30 from dual union
14 select 5, 11, 40 from dual union
15 select 5, 12, 50 from dual union
16 select 5, 13, 60 from dual
17 ),
18 inter as
19 (select concert_id, event_id, count(customer_id) attendance,
20 avg(count(*)) over (partition by concert_id) avg_attendance_each_concert
21 from booking
22 group by concert_id, event_id
23 )
24 select concert_id, event_id, attendance, avg_attendance_each_concert
25 from inter
26 where attendance < avg_attendance_Each_concert
27 order by event_id;
CONCERT_ID EVENT_ID ATTENDANCE AVG_ATTENDANCE_EACH_CONCERT
---------- ---------- ---------- ---------------------------
1 1 1 1,5
5 12 1 2
5 13 1 2
SQL>

How to sum two different fields from two tables with one field is common

I have two tables Sales and Charges.
Tables having data as:
'Sales' 'Charges'
SID F_AMT SID C_AMT
1 100 1 10
1 100 1 10
1 100 1 20
1 200 2 20
2 200 2 10
2 300 3 20
4 300 3 30
4 300 3 10
4 300 5 20
4 200 5 10
I want the output as below:
SID Total_Fees Total_charges
1 500 40
2 500 30
3 0 60
4 1100 0
5 0 30
Assuming you want to do it for the whole tables this is the simplest approach:
Select Sid
, Sum(f_amt) as total_fees
, Sum(c_amt) as total_charges
From ( select sid, f_amt, 0 as c_amt
From sales
Union all
select sid, 0 as f_amt, c_amt
From charges
)
Group by sid
Use full join and nvl():
select sid, nvl(sum(f_amt), 0) fees, nvl(sum(c_amt), 0) charges
from sales s
full join charges c using (sid)
group by sid
order by sid
Demo:
with sales(sid, f_amt) as (
select 1, 100 from dual union all select 1, 100 from dual union all
select 1, 100 from dual union all select 1, 200 from dual union all
select 2, 200 from dual union all select 2, 300 from dual union all
select 4, 300 from dual union all select 4, 300 from dual union all
select 4, 300 from dual union all select 4, 200 from dual ),
charges (sid, c_amt) as (
select 1, 10 from dual union all select 1, 10 from dual union all
select 1, 20 from dual union all select 2, 20 from dual union all
select 2, 10 from dual union all select 3, 20 from dual union all
select 3, 30 from dual union all select 3, 10 from dual union all
select 5, 20 from dual union all select 5, 10 from dual )
select sid, nvl(sum(f_amt), 0) fees, nvl(sum(c_amt), 0) charges
from sales s
full join charges c using (sid)
group by sid
order by sid
Output:
SID FEES CHARGES
------ ---------- ----------
1 1500 160
2 1000 60
3 0 60
4 1100 0
5 0 30
You could use conditional aggregation:
SELECT SID,
COALESCE(SUM(CASE WHEN t=1 THEN AMT END),0) AS Total_Fees,
COALESCE(SUM(CASE WHEN t=2 THEN AMT END),0) AS Total_Charges
FROM (SELECT SID, F_AMT AS AMT, 1 AS t
FROM Sales
UNION ALL
SELECT SID, C_AMT AS AMT, 2 AS t
FROM Charges) sub
GROUP BY SID
ORDER BY SID;
DB Fiddle Demo