Oracle - How to count average of attendance on based on concert itself? - sql

How to count an average of attendance based on concert_id?
So far what i have done is like this
select concert_id, event_id, count(customer_id) attendance,
case when concert_id = 1
then (select count(customer_id)/count(concert_id)
from booking where concert_id=1)
end as avg_attendance_each_concert
from booking
group by event_id, concert_id
order by event_id;
result
CONCERT_ID EVENT_ID ATTENDANCE AVG_ATTENDANCE_EACH_CONCERT
---------- ---------- ---------- ---------------------------
1 1 1 3
1 2 2 3
2 3 2
2 4 1
3 5 2
3 6 2
4 8 2
4 9 2
5 11 4
5 12 1
5 13 1
How to make AVG_ATTENDANCE_EACH_CONCERT become like this?
CONCERT_ID EVENT_ID ATTENDANCE AVG_ATTENDANCE_EACH_CONCERT
---------- ---------- ---------- ---------------------------
1 1 1 1.5 --> 3 attendance / 2 same concert_id
1 2 2 1.5
2 3 2 1.5 --> 3 attendance / 2 same concert_id
2 4 1 1.5
3 5 2 2 --> 4 attendance / 2 same concert_id
3 6 2 2
4 8 2 2 --> 4 attendance / 2 same concert_id
4 9 2 2
5 11 4 2 --> 6 attendance / 3 same concert_id
5 12 1 2
5 13 1 2
Because I would like to show which event have below average attendance

How about AVG in its analytic form?
(By the way, your example for CONCERT_ID = 5 is wrong; 6 / 3 = 2, not 3).
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 select concert_id, event_id, count(customer_id) attendance,
19 avg(count(*)) over (partition by concert_id) avg_attendance_each_concert
20 from booking
21 group by concert_id, event_id
22 order by event_id;
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
7 rows selected.
SQL>

Related

How to increase date by 1 month in Oracle sql

I want to add 1 month for loop by subscribed month to get each customers monthly payment date.
I have a table like this:
ID
REGISTER DATE
SUBSCRIBED MONTH
1
2022.01.01
3
2
2022.07.01
6
I want to have result like this:
ID
REGISTER DATE
SUBSCRIBED MONTH
MUST PAY DATE
1
2022.01.01
3
2022.01.01
1
2022.01.01
3
2022.02.01
1
2022.01.01
3
2022.03.01
2
2022.07.01
6
2022.07.01
2
2022.07.01
6
2022.08.01
2
2022.07.01
6
2022.09.01
2
2022.07.01
6
2022.10.01
2
2022.07.01
6
2022.11.01
2
2022.07.01
6
2022.12.01
Tried this, but returning duplicated.
SELECT ID, ADDMONTHS(REGISTER_DATE,LEVEL) FROM SUBLIST CONNECT BY LEVEL<=SUB_MONTH
Any help will be appreciated. Many thanks.
Here's one option:
Setting date format (you don't have to do that):
SQL> alter session set nls_date_format = 'yyyy.mm.dd';
Session altered.
Sample data:
SQL> with test (id, register_date, subscribed_month) as
2 (select 1, date '2022-01-01', 3 from dual union all
3 select 2, date '2022-07-01', 6 from dual
4 )
Query begins here:
5 select id, register_date, subscribed_month,
6 add_months(register_date, column_value - 1) must_pay_date
7 from test cross join table(cast(multiset(select level from dual
8 connect by level <= subscribed_month
9 ) as sys.odcinumberlist))
10 order by id, register_date, must_pay_date;
ID REGISTER_D SUBSCRIBED_MONTH MUST_PAY_D
---------- ---------- ---------------- ----------
1 2022.01.01 3 2022.01.01
1 2022.01.01 3 2022.02.01
1 2022.01.01 3 2022.03.01
2 2022.07.01 6 2022.07.01
2 2022.07.01 6 2022.08.01
2 2022.07.01 6 2022.09.01
2 2022.07.01 6 2022.10.01
2 2022.07.01 6 2022.11.01
2 2022.07.01 6 2022.12.01
9 rows selected.
SQL>
You can inner join your data to a subquery containing rows with numbers of months to be added:
WITH
tbl (ID, REGISTER_DATE, SUBSCRIBED_MONTH) AS
(
Select 1, DATE '2022-01-01', 3 From Dual Union All
Select 2, DATE '2022-07-01', 6 From Dual
)
SELECT t0.ID, t0.REGISTER_DATE, t0.SUBSCRIBED_MONTH,
Add_Months(t0.REGISTER_DATE, t1.MNTHS - 1) "PAY_DATE",
t1.MNTHS "PAYMENT_NO"
FROM tbl t0
INNER JOIN ( Select DISTINCT ID, LEVEL "MNTHS" From tbl Connect By LEVEL <= SUBSCRIBED_MONTH ) t1 ON(t1.ID = t0.ID)
ORDER BY t0.ID, t0.REGISTER_DATE, t1.MNTHS
Which with your sample data
WITH
tbl (ID, REGISTER_DATE, SUBSCRIBED_MONTH) AS
(
Select 1, DATE '2022-01-01', 3 From Dual Union All
Select 2, DATE '2022-07-01', 6 From Dual
)
... results as ...
ID REGISTER_DATE SUBSCRIBED_MONTH PAY_DATE PAYMENT_NO
---------- ------------- ---------------- --------- ----------
1 01-JAN-22 3 01-JAN-22 1
1 01-JAN-22 3 01-FEB-22 2
1 01-JAN-22 3 01-MAR-22 3
2 01-JUL-22 6 01-JUL-22 1
2 01-JUL-22 6 01-AUG-22 2
2 01-JUL-22 6 01-SEP-22 3
2 01-JUL-22 6 01-OCT-22 4
2 01-JUL-22 6 01-NOV-22 5
2 01-JUL-22 6 01-DEC-22 6

Identifying the transitive match records in oracle SQL

SOURCE
Rowid_object
Rowid_object_matched
1
2
1
3
3
2
2
4
4
6
6
5
7
8
9
8
Target
Rowid_object
Rowid_object_matched
1
1
2
1
3
1
4
1
5
1
6
1
7
7
8
7
9
7
Here, we have Source like data and we want Target like result.
There are two groups in source which are in transitive match.
Need to identify these kind of record.
In Oracle, you can use:
SELECT rowid_object,
MIN(root) AS rowid_object_matched
FROM (
SELECT CONNECT_BY_ROOT(rowid_object) AS root,
rowid_object,
rowid_object_matched
FROM source
CONNECT BY NOCYCLE
PRIOR rowid_object IN (rowid_object, rowid_object_matched)
OR PRIOR rowid_object_matched IN (rowid_object, rowid_object_matched)
)
UNPIVOT (
rowid_object FOR key IN (rowid_object, rowid_object_matched)
)
GROUP BY rowid_object;
Which, for the sample data:
CREATE TABLE SOURCE (Rowid_object, Rowid_object_matched) AS
SELECT 1, 2 FROM DUAL UNION ALL
SELECT 1, 3 FROM DUAL UNION ALL
SELECT 3, 2 FROM DUAL UNION ALL
SELECT 2, 4 FROM DUAL UNION ALL
SELECT 4, 6 FROM DUAL UNION ALL
SELECT 6, 5 FROM DUAL UNION ALL
SELECT 7, 8 FROM DUAL UNION ALL
SELECT 9, 8 FROM DUAL;
Outputs:
ROWID_OBJECT
ROWID_OBJECT_MATCHED
1
1
2
1
3
1
4
1
6
1
5
1
7
7
8
7
9
7
fiddle

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

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

Reset Row Number in Oracle conditionally

I have this record set returned , now I want to have a row number column which gets reset after every 3rd row. Can anyone help me with this? needs to be done with Oracle SQL.
Explanation below-
data
current row number
rquired row number
Chris
1
1
Bryan
2
2
Jim
3
3
Davis
4
1
Kia
5
2
Jones
6
3
Mary
7
1
Carrie
8
2
Pearce
9
3
Cesar
10
1
Bob
11
2
You can mod the current value:
mod(current_row_num - 1, 3) + 1
So using a CTE to represent your current result set:
with your_result (data, current_row_num) as (
select 'Chris', 1 from dual union all
select 'Bryan', 2 from dual union all
select 'Jim', 3 from dual union all
select 'Davis', 4 from dual union all
select 'Kia', 5 from dual union all
select 'Jones', 6 from dual union all
select 'Mary', 7 from dual union all
select 'Carrie', 8 from dual union all
select 'Pearce', 9 from dual union all
select 'Cesar', 10 from dual union all
select 'Bob', 11 from dual
)
select data, current_row_num, mod(current_row_num - 1, 3) + 1 as required_row_num
from your_result
order by current_row_num
DATA
CURRENT_ROW_NUM
REQUIRED_ROW_NUM
Chris
1
1
Bryan
2
2
Jim
3
3
Davis
4
1
Kia
5
2
Jones
6
3
Mary
7
1
Carrie
8
2
Pearce
9
3
Cesar
10
1
Bob
11
2
db<>fiddle

Create a new column and flag as 1 or 0?

Table:
PARENT_ID ID YR_MONTH REWARD
1 1 11 201601 3
2 1 11 201605 9
3 1 11 201609 12
4 1 12 201601 6
5 1 12 201605 9
6 1 12 201609 9
7 2 21 201601 15
8 2 21 201605 9
9 2 21 201609 12
10 2 22 201601 9
10 2 22 201605 9
10 2 23 201609 9
I need to create a new column based on the reward column. Where Reward is 9 put 1 else 0 based on the condition.
Expected results
output table -
PARENT_ID ID YR_MONTH REWARD REWARD_STATUS
1 1 11 201601 3 0
2 1 11 201605 9 1
3 1 11 201609 12 0
4 1 12 201601 6 0
5 1 12 201605 9 1
6 1 12 201609 9 0
7 2 21 201601 15 0
8 2 21 201605 9 0
9 2 21 201609 12 0
10 2 22 201601 9 1
10 2 22 201605 9 0
10 2 23 201609 9 1
for particular parent_id, id check if there is no higher reward than 9 in any of previous yr_month if yes then 0 else 1.
I am interpreting this as "if the previous year_month is higher than 9 then 1 else 0."
I think you want lag():
select t.*,
(case when lag(reward) over (partition by parent_id order by year_month) > 9
then 1 else 0
end) as reward_status
from t;
with t (PARENT_ID, ID, YR_MONTH, REWARD) as (
select 1, 11, 201601, 3 from dual union all
select 1, 11, 201605, 9 from dual union all
select 1, 11, 201609, 12 from dual union all
select 1, 12, 201601, 6 from dual union all
select 1, 12, 201605, 9 from dual union all
select 1, 12, 201609, 9 from dual union all
select 2, 21, 201601, 15 from dual union all
select 2, 21, 201605, 9 from dual union all
select 2, 21, 201609, 12 from dual union all
select 2, 22, 201601, 9 from dual union all
select 2, 22, 201605, 9 from dual union all
select 2, 23, 201609, 9 from dual
)
select t1.*, case when reward = 9 and grp = 1 then 1 else 0 end reward_status from (
select t.*, sum(case when reward < 9 then 0 else 1 end) over (partition by parent_id, id order by yr_month) grp
from t
) t1
order by parent_id, id, yr_month;
First, group the rows based on your condition. Here, I have used the SUM() analytic function for that purpose. Then in the outer query, simply match if 9 is the first to occur in a group.