How to increase date by 1 month in Oracle sql - 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

Related

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

List number into TABLE records by the value in another table

Sorry for a little bit confuse on the title.
I would like to use SQL for join sequence table as below
Original Table Records
3
3
4
1
Result Table
1
2
3
1
2
3
1
2
3
4
1
Based on comment you posted,
sample data:
SQL> with test (col) as
2 (select 3 from dual union all
3 select 3 from dual union all
4 select 4 from dual union all
5 select 1 from dual
6 )
Query begins here:
7 select column_value
8 from test cross join
9 table(cast(multiset(select level from dual
10 connect by level <= col
11 ) as sys.odcinumberlist));
COLUMN_VALUE
------------
1
2
3
1
2
3
1
2
3
4
1
11 rows selected.
SQL>

Merge Missing Dates With The Actual Query

I am working on a query where a database may have all dates with corresponding data or may not have. Data in the
table are as follows:
ID DATE
1 6/1/2021
1 6/2/2021
1 6/3/2021
1 6/4/2021
1 6/5/2021
1 6/8/2021
2 6/4/2021
2 6/5/2021
2 6/8/2021
Expected Output:
ID DATE
1 6/1/2021
1 6/2/2021
1 6/3/2021
1 6/4/2021
1 6/5/2021
1 6/6/2021
1 6/7/2021
1 6/8/2021
2 6/1/2021
2 6/2/2021
2 6/3/2021
2 6/4/2021
2 6/5/2021
2 6/6/2021
2 6/7/2021
2 6/8/2021
So I tried the following query with LEFT JOIN that'll return all required date:
WITH all_dates AS (SELECT TO_DATE('01-JUN-2021') + ROWNUM - 1 AS d FROM dual CONNECT BY ROWNUM <= ADD_MONTHS(TO_DATE('01-JUN-2021'), 12 ) - TO_DATE('01-JUN-2021'))
SELECT T.ID, T.DATE FROM all_dates LEFT JOIN TABLE_HERE t on T.DATE = all_dates.d WHERE all_dates.d <= '08-JUN-2021' AND T.ID ('1', '2') AND T.DATE >= '01-JUN-2021' AND T.DATE <= '08-JUN-2021' ORDER BY all_dates.d;
Unfortunately this only returns data with matching dates, not the missing one (Missing one will be merged with the actual). Is there anything that I require to do to make it work?
To me, it looks as the following query; read comments within code:
SQL> with
2 your (id, datum) as
3 -- your sample data
4 (select 1, date '2021-06-01' from dual union all
5 select 1, date '2021-06-02' from dual union all
6 select 1, date '2021-06-08' from dual union all
7 --
8 select 2, date '2021-06-08' from dual union all
9 select 2, date '2021-06-04' from dual union all
10 select 2, date '2021-06-08' from dual
11 ),
12 calendar as
13 -- you already know how to create a calendar; I'm using only 10 days for simplicity
14 (select date '2021-06-01' + level - 1 datum
15 from dual
16 connect by level <= 10
17 ),
18 ids (id) as
19 -- distinct ID values from your sample table (returns two rows; "1" and "2")
20 (select distinct id from your)
21 -- final query: cross join of calendar and distinct ID values
22 select c.datum, i.id
23 from calendar c cross join ids i
24 order by i.id, c.datum;
The result is
DATUM ID
-------- ----------
01.06.21 1
02.06.21 1
03.06.21 1
04.06.21 1
05.06.21 1
06.06.21 1
07.06.21 1
08.06.21 1
09.06.21 1
10.06.21 1
01.06.21 2
02.06.21 2
03.06.21 2
04.06.21 2
05.06.21 2
06.06.21 2
07.06.21 2
08.06.21 2
09.06.21 2
10.06.21 2
20 rows selected.
SQL>

generate sequence number

Hi can any one help me generate version number based on below table data
ex:
book_name version
math_1 1
physics_1 3
physics_6 4
chemist_1 1
chemist_2 2
eng_1 1
i want a query to rerun below result based on above table data( especially Physics )
expected result
book_name version
math_1 1
physics_1 1
physics_6 2
chemist_1 1
chemist_2 2
eng_1 1
Thanks in advance
To produce the new version you need to separate the subject from the level. Once you do that, a simple ROW_NUMBER() function does the job.
For example:
select x.*,
row_number() over(partition by subject order by lvl) as new_version
from (
select t.*,
substr(book_name, 1, instr(book_name, '_') - 1) as subject,
to_number(substr(book_name, 1 + instr(book_name, '_'))) as lvl
from t
) x;
Result:
BOOK_NAME version SUBJECT LVL NEW_VERSION
----------- --------- --------- ----- -------------
chemist_1 1 chemist 1 1
chemist_2 2 chemist 2 2
eng_1 1 eng 1 1
math_1 1 math 1 1
physics_1 3 physics 1 1
physics_6 4 physics 6 2
See running example at db<>fiddle.
Similarly (lines #1 - 8 represent sample data; you don't type that. Query that returns result you wanted begins at line #9):
SQL> with test (book_name, version) as
2 (select 'math_1' , 1 from dual union all
3 select 'physics_1', 3 from dual union all
4 select 'physics_6', 4 from dual union all
5 select 'chemist_1', 1 from dual union all
6 select 'chemist_2', 2 from dual union all
7 select 'eng_1' , 1 from dual
8 )
9 select book_name,
10 version,
11 row_number() over (partition by regexp_substr(book_name, '[[:alpha:]]')
12 order by to_number(regexp_substr(book_name, '\d+$'))
13 ) new_version
14 from test
15 order by book_name, new_version;
BOOK_NAME VERSION NEW_VERSION
--------- ---------- -----------
chemist_1 1 1
chemist_2 2 2
eng_1 1 1
math_1 1 1
physics_1 3 1
physics_6 4 2
6 rows selected.
SQL>
You seem to just want the last digits:
select t.*,
regexp_substr(book_name, '[0-9]+$')
from t;
Here is a db<>fiddle.

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

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>