How to convert column with Rank Value into rows in oracle - sql

I've some query, that returns following data:
Month Year EMP_ID EMP_NAME RANK
Jan 2019 1 EmpName1 1
Jan 2019 2 EmpName2 2
Jan 2019 3 EmpName3 3
Jan 2019 4 EmpName4 4
Jan 2019 5 EmpName5 5
FEB 2019 1 EmpName1 1
FEB 2019 2 EmpName2 2
FEB 2019 3 EmpName3 3
FEB 2019 4 EmpName4 4
FEM 2019 5 EmpName5 5
I want to display this data in the following format:
Month Year Rank1 Rank2 Rank3 Rank4 Rank5
Jan 2019 EmpName1 EmpNAme2 EmpName3 EmpName4 EmpName5
Feb 2019 EmpName1 EmpNAme2 EmpName3 EmpName4 EmpName5

If version Oracle 11+ to use pivot
with tbl as
(SELECT 'JAN' mon, '2019' y, '1' rn, 'EmpName1' name from dual
UNION ALL
select 'JAN', '2019', '2', 'EmpName2' from dual
union all
select 'JAN', '2019', '3', 'EmpName3' from dual
union all
select 'JAN', '2019', '4', 'EmpName4' from dual
union all
select 'JAN', '2019', '5', 'EmpName5' from dual
union all
select 'FEB', '2019', '1', 'EmpName1' from dual
union all
select 'FEB', '2019', '2', 'EmpName2' from dual
union all
select 'FEB', '2019', '3', 'EmpName3' from dual
union all
select 'FEB', '2019', '4', 'EmpName4' from dual
union all
select 'FEB', '2019', '5', 'EmpName5' from dual)
select *
from (select mon, y, rn, name
from tbl)
pivot
(
MAX(name)
FOR rn
IN ('1', '2', '3', '4', '5')
)
ORDER BY 1
if 10g
with tbl as
(SELECT 'JAN' mon, '2019' y, '1' rn, 'EmpName1' name from dual
UNION ALL
select 'JAN', '2019', '2', 'EmpName2' from dual
union all
select 'JAN', '2019', '3', 'EmpName3' from dual
union all
select 'JAN', '2019', '4', 'EmpName4' from dual
union all
select 'JAN', '2019', '5', 'EmpName5' from dual
union all
select 'FEB', '2019', '1', 'EmpName1' from dual
union all
select 'FEB', '2019', '2', 'EmpName2' from dual
union all
select 'FEB', '2019', '3', 'EmpName3' from dual
union all
select 'FEB', '2019', '4', 'EmpName4' from dual
union all
select 'FEB', '2019', '5', 'EmpName5' from dual)
select
mon,
y,
min(decode(rn, 1, nm, NULL)) Rank1 ,
min(decode(rn, 2, nm, NULL)) Rank2 ,
min(decode(rn, 3, nm, NULL)) Rank3 ,
min(decode(rn, 4, nm, NULL)) Rank4 ,
min(decode(rn, 5, nm, NULL)) Rank5
from
(
select
tbl.mon,
tbl.y,
tbl.rn,
max(name) nm
from
tbl
group by tbl.mon, tbl.y, tbl.rn
)
group by mon, y

Just use Conditional Aggregation with case..when expressions :
select Month, Year,
max( case when emp_id = 1 then Emp_Name end ) as EmpName1,
max( case when emp_id = 2 then Emp_Name end ) as EmpName2,
max( case when emp_id = 3 then Emp_Name end ) as EmpName3,
max( case when emp_id = 4 then Emp_Name end ) as EmpName4,
max( case when emp_id = 5 then Emp_Name end ) as EmpName5
from tab
group by Month, Year

Related

Which statement to use based on its row and transaction in stay that way

I am getting stuck on this, hard to explain to write down here as well the subject heading hopefully by the data example and output result below that enough to give you an idea.
I am run out which Oracle SQL statement best to use. The over (partition) seem didn't work out for the below scenario.
Before & After:
DATA
customer_id
row_id
date
type
LB01
22
14/03/2022
CAR 1
LB01
21
11/03/2022
CAR 1
LB01
20
9/11/2001
CAR 1
LB01
19
16/10/2001
CAR 1
LB01
18
10/08/2001
CAR 2
LB01
17
29/05/2001
CAR 2
LB01
16
24/04/2001
CAR 2
LB01
15
13/03/2001
CAR 3
LB01
14
21/12/2000
CAR 3
LB01
13
13/11/2000
CAR 3
LB01
12
20/10/2000
CAR 3
LB01
11
14/03/2000
CAR 1
LB01
10
18/01/2000
CAR 1
LB01
9
24/12/1999
CAR 2
LB01
8
14/09/1999
CAR 2
LB01
7
30/03/1999
CAR 2
LB01
6
24/02/1999
CAR 2
LB01
5
19/02/1999
CAR 4
LB01
4
15/12/1998
CAR 4
LB01
3
15/12/1998
CAR 4
LB01
2
24/09/1998
CAR 4
LB01
1
06/08/1998
CAR 4
The output that I want result like this:
customer_id
type
min_date
max_date
LB01
Car 1
16/10/2001
14/03/2002
LB01
Car 2
24/04/2001
10/08/2001
LB01
Car 3
20/10/2000
13/03/2001
LB01
Car 1
18/01/2000
14/03/2000
LB01
Car 2
24/02/1999
24/12/1999
LB01
Car 4
6/08/1998
19/02/1999
I hope those makes sense for you and hopefully you can assist me on this.
You have to apply Tabibitosan algorithm first:
select customer_id, type, rn, min(dat) as min_date, max(dat) as max_dat
from (
select d.*, row_id+1-row_number() over(partition by customer_id, type order by row_id) as rn
from data d
)
group by customer_id, type, rn
order by customer_id, type
;
CUST TYPE RN MIN_DATE MAX_DAT
---- ----- ---------- -------- --------
LB01 CAR 1 10 18/01/00 14/03/00
LB01 CAR 1 17 16/10/01 14/03/22
LB01 CAR 2 6 24/02/99 24/12/99
LB01 CAR 2 12 24/04/01 10/08/01
LB01 CAR 3 12 20/10/00 13/03/01
LB01 CAR 4 1 06/08/98 19/02/99
Just create your own grouping calculation out of already existing ROW_ID and use MIN_DATE for ordering:
SELECT CUST_ID, A_TYPE, Min(A_DATE) "MIN_DATE", Max(A_DATE) "MAX_DATE"
FROM (SELECT t.*,
Max(ROW_ID) OVER(Partition By CUST_ID || A_TYPE ORDER BY ROW_ID DESC Rows Between Unbounded Preceding And Current Row) -
(ROW_ID + Sum(1) OVER(PARTITION BY CUST_ID || A_TYPE ORDER BY ROW_ID DESC) - 1) "GRP"
FROM tbl t
ORDER BY ROW_ID DESC )
GROUP BY CUST_ID, A_TYPE, GRP
ORDER BY CUST_ID, MIN(A_DATE) DESC
With your sample data:
WITH
tbl (CUST_ID, ROW_ID, A_DATE, A_TYPE) AS
( Select 'LB01', 22, To_Date('14/03/2022', 'dd/mm/yyyy'), 'CAR 1' From Dual Union All
Select 'LB01', 21, To_Date('11/03/2022', 'dd/mm/yyyy'), 'CAR 1' From Dual Union All
Select 'LB01', 20, To_Date('09/11/2001', 'dd/mm/yyyy'), 'CAR 1' From Dual Union All
Select 'LB01', 19, To_Date('16/10/2001', 'dd/mm/yyyy'), 'CAR 1' From Dual Union All
--
Select 'LB01', 18, To_Date('10/08/2001', 'dd/mm/yyyy'), 'CAR 2' From Dual Union All
Select 'LB01', 17, To_Date('29/05/2001', 'dd/mm/yyyy'), 'CAR 2' From Dual Union All
Select 'LB01', 16, To_Date('24/04/2001', 'dd/mm/yyyy'), 'CAR 2' From Dual Union All
--
Select 'LB01', 15, To_Date('13/03/2001', 'dd/mm/yyyy'), 'CAR 3' From Dual Union All
Select 'LB01', 14, To_Date('21/12/2000', 'dd/mm/yyyy'), 'CAR 3' From Dual Union All
Select 'LB01', 13, To_Date('13/11/2000', 'dd/mm/yyyy'), 'CAR 3' From Dual Union All
Select 'LB01', 12, To_Date('20/10/2000', 'dd/mm/yyyy'), 'CAR 3' From Dual Union All
--
Select 'LB01', 11, To_Date('14/03/2000', 'dd/mm/yyyy'), 'CAR 1' From Dual Union All
Select 'LB01', 10, To_Date('18/01/2000', 'dd/mm/yyyy'), 'CAR 1' From Dual Union All
--
Select 'LB01', 9, To_Date('24/12/1999', 'dd/mm/yyyy'), 'CAR 2' From Dual Union All
Select 'LB01', 8, To_Date('14/09/1999', 'dd/mm/yyyy'), 'CAR 2' From Dual Union All
Select 'LB01', 7, To_Date('30/03/1999', 'dd/mm/yyyy'), 'CAR 2' From Dual Union All
Select 'LB01', 6, To_Date('24/02/1999', 'dd/mm/yyyy'), 'CAR 2' From Dual Union All
--
Select 'LB01', 5, To_Date('19/02/1999', 'dd/mm/yyyy'), 'CAR 4' From Dual Union All
Select 'LB01', 4, To_Date('15/12/1998', 'dd/mm/yyyy'), 'CAR 4' From Dual Union All
Select 'LB01', 3, To_Date('15/12/1998', 'dd/mm/yyyy'), 'CAR 4' From Dual Union All
Select 'LB01', 2, To_Date('24/09/1998', 'dd/mm/yyyy'), 'CAR 4' From Dual Union All
Select 'LB01', 1, To_Date('06/08/1998', 'dd/mm/yyyy'), 'CAR 4' From Dual
)
R e s u l t :
CUST_ID
A_TYPE
MIN_DATE
MAX_DATE
LB01
CAR 1
16-OCT-01
14-MAR-22
LB01
CAR 2
24-APR-01
10-AUG-01
LB01
CAR 3
20-OCT-00
13-MAR-01
LB01
CAR 1
18-JAN-00
14-MAR-00
LB01
CAR 2
24-FEB-99
24-DEC-99
LB01
CAR 4
06-AUG-98
19-FEB-99
PLEASE NOTE: For some reason this website can't tick both answers that the solution provided by #p3consulting and #d r are working on my data by creating a new column for a new row id in that way row numbers are in orderly
subquery before that;
row_number() over(partition by cust_id order by seq_id ) row_id
depend on how your data has numbers are not in orderly number
refer to the above post made by #p3consulting regarding the Tabibitosan algorithm
here is the below script that you can copy and paste into your Oracle SQL to learn
for my question here still does not work for my data and still searching the right script
with
sample_data (customer_id, row_id, mock_date, product_type) as (
select 'LB01', '22', to_date ('03/14/22', 'mm/dd/rr'), 'CAR 1' from dual union all
select 'LB01', '21', to_date ('03/11/22', 'mm/dd/rr'), 'CAR 1' from dual union all
select 'LB01', '20', to_date ('11/09/01', 'mm/dd/rr'), 'CAR 1' from dual union all
select 'LB01', '19', to_date ('10/16/01', 'mm/dd/rr'), 'CAR 1' from dual union all
select 'LB01', '18', to_date ('08/10/01', 'mm/dd/rr'), 'CAR 2' from dual union all
select 'LB01', '17', to_date ('05/29/01', 'mm/dd/rr'), 'CAR 2' from dual union all
select 'LB01', '16', to_date ('04/24/01', 'mm/dd/rr'), 'CAR 2' from dual union all
select 'LB01', '15', to_date ('03/13/01', 'mm/dd/rr'), 'CAR 3' from dual union all
select 'LB01', '14', to_date ('12/21/00', 'mm/dd/rr'), 'CAR 3' from dual union all
select 'LB01', '13', to_date ('11/13/00', 'mm/dd/rr'), 'CAR 3' from dual union all
select 'LB01', '12', to_date ('10/20/00', 'mm/dd/rr'), 'CAR 3' from dual union all
select 'LB01', '11', to_date ('03/14/00', 'mm/dd/rr'), 'CAR 1' from dual union all
select 'LB01', '10', to_date ('01/18/00', 'mm/dd/rr'), 'CAR 1' from dual union all
select 'LB01', '09', to_date ('12/24/99', 'mm/dd/rr'), 'CAR 2' from dual union all
select 'LB01', '08', to_date ('09/14/99', 'mm/dd/rr'), 'CAR 2' from dual union all
select 'LB01', '07', to_date ('03/30/99', 'mm/dd/rr'), 'CAR 2' from dual union all
select 'LB01', '06', to_date ('02/24/99', 'mm/dd/rr'), 'CAR 2' from dual union all
select 'LB01', '05', to_date ('02/19/99', 'mm/dd/rr'), 'CAR 4' from dual union all
select 'LB01', '04', to_date ('12/15/98', 'mm/dd/rr'), 'CAR 4' from dual union all
select 'LB01', '03', to_date ('12/15/98', 'mm/dd/rr'), 'CAR 4' from dual union all
select 'LB01', '02', to_date ('09/24/98', 'mm/dd/rr'), 'CAR 4' from dual union all
select 'LB01', '01', to_date ('06/08/98', 'mm/dd/rr'), 'CAR 4' from dual
)
select
customer_id ,
product_type ,
grp ,
min(mock_date) as min_date ,
max(mock_date) as max_date
from (
select d.* ,
row_id + 1 - row_number () over (partition by customer_id, product_type order by row_id) as grp
from sample_data d )
group by
customer_id ,
product_type ,
grp
order by
grp desc

how to use windows function during merge in sql

I am working in oracle sql. I have two table which is linked to each other by one column - company_id (see on the picture); I want to merge table 1 to table 2 and calculate 6 month average (6 month before period from table 2) of income for each company_id and each date of table2. I appreciate any code/idea how to solve this task.
You can use an analytic range window to calculate the averages for table1 and then JOIN the result to table2:
SELECT t2.*,
t1.avg_income_6,
t1.avg_income_12
FROM table2 t2
LEFT OUTER JOIN (
SELECT company_id,
dt,
ROUND(AVG(income) OVER (
PARTITION BY company_id
ORDER BY dt
RANGE BETWEEN INTERVAL '5' MONTH PRECEDING
AND INTERVAL '0' MONTH FOLLOWING
), 2) AS avg_income_6,
ROUND(AVG(income) OVER (
PARTITION BY company_id
ORDER BY dt
RANGE BETWEEN INTERVAL '11' MONTH PRECEDING
AND INTERVAL '0' MONTH FOLLOWING
), 2) AS avg_income_12
FROM table1
) t1
ON (t2.company_id = t1.company_id AND t2.dt = t1.dt);
Which, for the sample data:
CREATE TABLE table1 (company_id, dt, income) AS
SELECT 1, date '2019-01-01', 65 FROM DUAL UNION ALL
SELECT 1, date '2019-02-01', 58 FROM DUAL UNION ALL
SELECT 1, date '2019-03-01', 12 FROM DUAL UNION ALL
SELECT 1, date '2019-04-01', 81 FROM DUAL UNION ALL
SELECT 1, date '2019-05-01', 38 FROM DUAL UNION ALL
SELECT 1, date '2019-06-01', 81 FROM DUAL UNION ALL
SELECT 1, date '2019-07-01', 38 FROM DUAL UNION ALL
SELECT 1, date '2019-08-01', 69 FROM DUAL UNION ALL
SELECT 1, date '2019-09-01', 54 FROM DUAL UNION ALL
SELECT 1, date '2019-10-01', 90 FROM DUAL UNION ALL
SELECT 1, date '2019-11-01', 10 FROM DUAL UNION ALL
SELECT 1, date '2019-12-01', 12 FROM DUAL UNION ALL
SELECT 1, date '2020-01-01', 11 FROM DUAL UNION ALL
SELECT 1, date '2020-02-01', 83 FROM DUAL UNION ALL
SELECT 1, date '2020-03-01', 18 FROM DUAL UNION ALL
SELECT 1, date '2020-04-01', 28 FROM DUAL UNION ALL
SELECT 1, date '2020-05-01', 52 FROM DUAL UNION ALL
SELECT 1, date '2020-06-01', 21 FROM DUAL UNION ALL
SELECT 1, date '2020-07-01', 54 FROM DUAL UNION ALL
SELECT 1, date '2020-08-01', 30 FROM DUAL UNION ALL
SELECT 1, date '2020-09-01', 12 FROM DUAL UNION ALL
SELECT 1, date '2020-10-01', 25 FROM DUAL UNION ALL
SELECT 1, date '2020-11-01', 86 FROM DUAL UNION ALL
SELECT 1, date '2020-12-01', 4 FROM DUAL UNION ALL
SELECT 1, date '2021-01-01', 10 FROM DUAL UNION ALL
SELECT 1, date '2021-02-01', 72 FROM DUAL UNION ALL
SELECT 1, date '2021-03-01', 65 FROM DUAL UNION ALL
SELECT 1, date '2021-04-01', 25 FROM DUAL;
CREATE TABLE table2 (company_id, dt) AS
SELECT 1, date '2019-06-01' FROM DUAL UNION ALL
SELECT 1, date '2019-09-01' FROM DUAL UNION ALL
SELECT 1, date '2019-12-01' FROM DUAL UNION ALL
SELECT 1, date '2020-01-01' FROM DUAL UNION ALL
SELECT 1, date '2020-07-01' FROM DUAL UNION ALL
SELECT 1, date '2020-08-01' FROM DUAL UNION ALL
SELECT 1, date '2021-03-01' FROM DUAL UNION ALL
SELECT 1, date '2021-04-01' FROM DUAL;
Outputs:
COMPANY_ID
DT
AVG_INCOME_6
AVG_INCOME_12
1
2019-06-01 00:00:00
55.83
55.83
1
2019-09-01 00:00:00
60.17
55.11
1
2019-12-01 00:00:00
45.5
50.67
1
2020-01-01 00:00:00
41
46.17
1
2020-07-01 00:00:00
42.67
41.83
1
2020-08-01 00:00:00
33.83
38.58
1
2021-03-01 00:00:00
43.67
38.25
1
2021-04-01 00:00:00
43.67
38
db<>fiddle here
I don't think you need any window function here (if you were thinking of analytic functions); ordinary avg with appropriate join conditions should do the job.
Sample data:
SQL> with
2 table1 (company_id, datum, income) as
3 (select 1, date '2019-01-01', 65 from dual union all
4 select 1, date '2019-02-01', 58 from dual union all
5 select 1, date '2019-03-01', 12 from dual union all
6 select 1, date '2019-04-01', 81 from dual union all
7 select 1, date '2019-05-01', 38 from dual union all
8 select 1, date '2019-06-01', 81 from dual union all
9 select 1, date '2019-07-01', 38 from dual union all
10 select 1, date '2019-08-01', 69 from dual union all
11 select 1, date '2019-09-01', 54 from dual union all
12 select 1, date '2019-10-01', 90 from dual union all
13 select 1, date '2019-11-01', 10 from dual union all
14 select 1, date '2019-12-01', 12 from dual
15 ),
16 table2 (company_id, datum) as
17 (select 1, date '2019-06-01' from dual union all
18 select 1, date '2019-09-01' from dual union all
19 select 1, date '2019-12-01' from dual union all
20 select 1, date '2020-01-01' from dual union all
21 select 1, date '2020-07-01' from dual
22 )
Query begins here:
23 select b.company_id,
24 b.datum ,
25 round(avg(a.income), 2) result
26 from table1 a join table2 b on a.company_id = b.company_id
27 and a.datum > add_months(b.datum, -6)
28 and a.datum <= b.datum
29 group by b.company_id, b.datum;
COMPANY_ID DATUM RESULT
---------- -------- ----------
1 01.06.19 55,83
1 01.09.19 60,17
1 01.12.19 45,5
1 01.01.20 47
SQL>

SQL Oracle - Sales Forecast

I am doing a Seles forecast in SQL Oracle. I need to figure out what revenue I can expect for next year. I should calculate for each month(In my example January, February 2018 for Each customer by State/City) . I have data for 3 years.
The result should contain an estimated sales forecast for each month based on the city+state combination. I was trying to use use req_slope, but it doesn't work. I have code here: SQL Fiddle
select c.*,
max(year) +1 forecast_year,
regr_slope(revenue, year)
* (max(year) + 1)
+ regr_intercept(revenue, year) forecasted_revenue
from New_customer_data c
group by Cust_ID ,
State ,
City ,
year ,
id_month ,
revenue ;
I need to figure out what revenue I can expect for next year.
Remove revenue and year from the GROUP BY clause as those are the columns you want to perform the regression on:
select cust_id,
city,
state,
id_month,
max(year) +1 forecast_year,
regr_slope(revenue, year)
* (max(year) + 1)
+ regr_intercept(revenue, year) forecasted_revenue
from New_customer_data c
group by
Cust_ID,
city,
state,
id_month;
Which, for your sample data:
insert into New_customer_data
select 1, 'MN' , 'Minneapolis', 2016, 1, 679862 from dual union all
select 1, 'IL', 'Chicago' , 2016, 2, 11862 from dual union all
select 1, 'MN' , 'Minneapolis', 2017, 1,547365 from dual union all
select 1, 'IL', 'Chicago' , 2017, 2, 705365 from dual union all
select 2, 'CA', 'San Diego', 2016, 1, 51074 from dual union all
select 2, 'CA', 'LA', 2016, 2, 598862 from dual union all
select 2, 'CA', 'San Diego', 2017, 1, 705365 from dual union all
select 2,'CA', 'LA', 2017, 2, 50611 from dual union all
select 3, 'CA', 'Santa Monica', 2016, 1, 190706 from dual union all
select 3, 'IL', 'Evanston', 2016, 2, 679862 from dual union all
select 3, 'CA', 'Santa Monica', 2017, 1, 705365 from dual union all
select 3, 'IL', 'Evanston', 2017, 2, 90393 from dual union all
select 4, 'MN', 'Shakopee', 2016, 1, 31649 from dual union all
select 4, 'FL', 'Miami', 2016, 2,888862 from dual union all
select 4, 'MN', 'Shakopee', 2017, 1, 125365 from dual union all
select 4, 'FL', 'Miami', 2017, 2, 30566 from dual;
Outputs:
CUST_ID
CITY
STATE
ID_MONTH
FORECAST_YEAR
FORECASTED_REVENUE
1
Minneapolis
MN
1
2018
414868
1
Chicago
IL
2
2018
1398868
2
San Diego
CA
1
2018
1359656
2
LA
CA
2
2018
-497640
3
Santa Monica
CA
1
2018
1220024
3
Evanston
IL
2
2018
-499076
4
Shakopee
MN
1
2018
219081
4
Miami
FL
2
2018
-827730
db<>fiddle here

SQL logic for Decompression

RDW (Retake Data Warehouse) used to reduce data volume by compression logic. Compression refers to storing physical data that only reflects changes to the underlying data source.
Inventory fact table store data in following form.
Week Item Location stock_on_hand
--------------------------------------------------
201601 I1 L1 50
201602 I1 L1 30
201605 I1 L1 60
201608 I1 L1 50
But I need sql query to get following result
Week Item Location stock_on_hand
--------------------------------------------------
201601 I1 L1 50
201602 I1 L1 30
201603 I1 L1 30
201604 I1 L1 30
201605 I1 L1 60
201606 I1 L1 60
201607 I1 L1 60
201608 I1 L1 50
Test data(I add several rows for better understanding and split year and week columns)
with t(year, Week , Item, Location, stock_on_hand) as
(select 2016, 01, 'I1', 'L1', 50 from dual union all
select 2016, 02, 'I1', 'L1', 30 from dual union all
select 2016 ,05, 'I1', 'L1', 60 from dual union all
select 2016 ,08, 'I1', 'L1', 50 from dual union all
select 2016, 02, 'I2', 'L1', 30 from dual union all
select 2016, 08, 'I2', 'L1', 40 from dual union all
select 2016, 02, 'I1', 'L2', 10 from dual union all
select 2016, 08, 'I1', 'L2', 40 from dual union all
select 2016, 08, 'I1', 'L3', 40 from dual)
Query
with t(year, Week , Item, Location, stock_on_hand) as
(select 2016, 01, 'I1', 'L1', 50 from dual union all
select 2016, 02, 'I1', 'L1', 30 from dual union all
select 2016 ,05, 'I1', 'L1', 60 from dual union all
select 2016 ,08, 'I1', 'L1', 50 from dual union all
select 2016, 02, 'I2', 'L1', 30 from dual union all
select 2016, 08, 'I2', 'L1', 40 from dual union all
select 2016, 02, 'I1', 'L2', 10 from dual union all
select 2016, 08, 'I1', 'L2', 40 from dual union all
select 2016, 08, 'I1', 'L3', 40 from dual),
temp(year, Week , Item, Location, stock_on_hand, ct) as(
select year, Week , Item, Location, stock_on_hand, nvl(lead(Week) over(partition by Item, Location order by year, Week)-Week,1) from t)
select year, Week + rn - 1 as week, Item, Location, stock_on_hand
from temp, xmltable('1 to xs:integer($ct)' passing ct as "ct" columns rn number path '.')
order by Item, Location ,year, week
This approach also have one minor. If you have interval in different year. Ex
select 2016, 01, 'I1', 'L1', 50 from dual union all
select 2017, 02, 'I1', 'L1', 30 from dual union all
Then it works incorrectly. I don't know whether you data has the same pattern. If it has then, please, add information to post or answer.
UPDATE
For intervals which lives in several years you can do follow(For startDate I choose date in international week)
with t(dateStart , Item, Location, stock_on_hand) as
(select to_date('28/12/2015', 'dd-mm-yyyy'), 'I1', 'L1', 50 from dual union all
select to_date('04/01/2016', 'dd-mm-yyyy'), 'I1', 'L1', 30 from dual union all
select to_date('25/01/2016', 'dd-mm-yyyy'), 'I1', 'L1', 60 from dual union all
select to_date('15/02/2016', 'dd-mm-yyyy'), 'I1', 'L1', 50 from dual union all
select to_date('01/01/2018', 'dd-mm-yyyy'), 'I1', 'L1', 30 from dual union all
select to_date('04/01/2016', 'dd-mm-yyyy'), 'I2', 'L1', 40 from dual union all
select to_date('15/02/2016', 'dd-mm-yyyy'), 'I2', 'L1', 10 from dual union all
select to_date('04/01/2016', 'dd-mm-yyyy'), 'I1', 'L2', 30 from dual union all
select to_date('15/02/2016', 'dd-mm-yyyy'), 'I1', 'L2', 40 from dual union all
select to_date('15/02/2016', 'dd-mm-yyyy'), 'I1', 'L3', 40 from dual),
temp(dateStart, Item, Location, stock_on_hand, ct) as(
select dateStart , Item, Location, stock_on_hand, nvl((lead(dateStart) over(partition by Item, Location order by dateStart)-dateStart)/7,1) from t)
select dateStart + (rn - 1)*7 as week, Item, Location, stock_on_hand
from temp, xmltable('1 to xs:integer($ct)' passing ct as "ct" columns rn number path '.')
order by Item, Location , dateStart

Cumulatively adding up sales from last years closing balance to current years opening balances

I am trying to cumulatively add sales from last years closing balance bringing it forward to first month and continuing like this from month to month. For instance if we have the following data:
select 4 id, 'A' name, 'group1' group, '2015' year, '10' month, 20 sales from dual union all
select 5 id, 'C' name,'group2' group, '2015' year, '12' month, 89 sales from dual union all
select 13 id,'B' name, 'group2' group, '2016' year, '01' month, 10 sales from dual union all
select 14 id,'A' name, 'group3' group, '2016' year, '02' month, 8 sales from dual union all
select 15 id,'B' name, 'group1' group, '2016' year, '02' month, 16 sales from dual union all
select 16 id,'D' name,'group2' group, '2016' year, '04' month, 15 sales from dual union all
select 17 id,'D' name,'group4' group, '2016' year, '05' month, 23 sales from dual union all
select 18 id,'D' name,'group3' group, '2016' year, '06' month, 39 sales from dual union all
select 19 id,'D' name,'group3' group, '2016' year, '07' month, 45 sales from dual union all
select 20 id,'D' name,'group3' group, '2016' year, '08' month, 12 sales from dual union all
select 21 id,'D' name,'group4' group, '2016' year, '09' month, 20 sales from dual union all
select 22 id,'D' name,'group3' group, '2016' year, '10' month, 4 sales from dual union all
select 23 id,'D' name,'group3' group, '2016' year, '11' month, 98 sales from dual union all
select 24 id,'D' name,'group4' group, '2016' year, '12' month, 70 sales from dual
Note, for Year=2015 the closing balance for that year is month=12 balance which in this case is 89 for group2 and 20 for group1. If we are in 2016, I want the cumulative query to return something like this:
year, month, group, opening_balance, closing_balance
2016 01 group2 89 99 (89+10)
2016 02 group3 0 8 (0+8)
2016 02 group1 20 36 (20 + 16)
2016 04 group2 99 114 (99 + 15)
2016 05 group4 0 23 (0 + 23 - note group4 has no prior balances)
2016 06 group3 8 47 (8 + 39)
2016 07 group3 47 92 (47 + 45)
and so on
This looks like it needs involving the analytical function sum() over (partition by .... order by ... )
But I haven't figured out the correct way to do this.
Thanks in advance.
Oracle Setup:
CREATE TABLE sales ( id, name, grp, year, month, sales ) AS
SELECT 4, 'A', 'group1', '2015', '10', 20 FROM DUAL UNION ALL
SELECT 5, 'C', 'group2', '2015', '12', 89 FROM DUAL UNION ALL
SELECT 13, 'B', 'group2', '2016', '01', 10 FROM DUAL UNION ALL
SELECT 14, 'A', 'group3', '2016', '02', 8 FROM DUAL UNION ALL
SELECT 15, 'B', 'group1', '2016', '02', 16 FROM DUAL UNION ALL
SELECT 16, 'D', 'group2', '2016', '04', 15 FROM DUAL UNION ALL
SELECT 17, 'D', 'group4', '2016', '05', 23 FROM DUAL UNION ALL
SELECT 18, 'D', 'group3', '2016', '06', 39 FROM DUAL UNION ALL
SELECT 19, 'D', 'group3', '2016', '07', 45 FROM DUAL UNION ALL
SELECT 20, 'D', 'group3', '2016', '08', 12 FROM DUAL UNION ALL
SELECT 21, 'D', 'group4', '2016', '09', 20 FROM DUAL UNION ALL
SELECT 22, 'D', 'group3', '2016', '10', 4 FROM DUAL UNION ALL
SELECT 23, 'D', 'group3', '2016', '11', 98 FROM DUAL UNION ALL
SELECT 24, 'D', 'group4', '2016', '12', 70 FROM DUAL;
Query:
SELECT *
FROM (
SELECT year,
month,
grp,
COALESCE(
SUM( sales ) OVER ( PARTITION BY grp
ORDER BY dt
RANGE BETWEEN UNBOUNDED PRECEDING
AND INTERVAL '1' MONTH PRECEDING
),
0
) AS opening_balance,
SUM( sales ) OVER ( PARTITION BY grp
ORDER BY dt
RANGE BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW
) AS closing_balance
FROM (
SELECT s.*,
TO_DATE( year || month, 'YYYYMM' ) AS dt
FROM sales s
)
)
WHERE year = 2016
ORDER BY year, month, grp;
Query - Alternative without RANGE BETWEEN:
SELECT *
FROM (
SELECT year,
month,
grp,
SUM( sales ) OVER ( PARTITION BY grp
ORDER BY year, month )
- sales AS opening_balance,
SUM( sales ) OVER ( PARTITION BY grp
ORDER BY year, month
) AS closing_balance
FROM sales
)
WHERE year = 2016
ORDER BY year, month, grp;
Output:
YEAR MO GRP OPENING_BALANCE CLOSING_BALANCE
---- -- ------ --------------- ---------------
2016 01 group2 89 99
2016 02 group1 20 36
2016 02 group3 0 8
2016 04 group2 99 114
2016 05 group4 0 23
2016 06 group3 8 47
2016 07 group3 47 92
2016 08 group3 92 104
2016 09 group4 23 43
2016 10 group3 104 108
2016 11 group3 108 206
2016 12 group4 43 113