SQL Oracle - Sales Forecast - sql

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

Related

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>

get unique matches

How can I get unique matches played between two teams
Game_Number, Team_A, Team_B, Date, Team_A_score, Team_B_score
1, IND, USA, 2020-01-01, 10, 20
2, USA, IND, 2020-01-02, 10, 20
3, AUS, IND, 2020-01-02, 30, 15
4, IND, AUS, 2020-01-03, 22, 34
5, UAE, AUS, 2020-01-04, 14, 41
Expected output
IND, USA
AUS, IND
UAE, AUS
In above case 1 and 2 games will be considered unique
If you do not mind the order of the teams then use DISTINCT with GREATEST and LEAST:
SELECT DISTINCT
LEAST(Team_A, Team_B) AS team_a,
GREATEST(Team_A, Team_B) AS team_b
FROM table_name
Which, for the sample data:
CREATE TABLE table_name (Game_Number, Team_A, Team_B, "DATE", Team_A_score, Team_B_score) AS
SELECT 1, 'IND', 'USA', DATE '2020-01-01', 10, 20 FROM DUAL UNION ALL
SELECT 2, 'USA', 'IND', DATE '2020-01-02', 10, 20 FROM DUAL UNION ALL
SELECT 3, 'AUS', 'IND', DATE '2020-01-02', 30, 15 FROM DUAL UNION ALL
SELECT 4, 'IND', 'AUS', DATE '2020-01-03', 22, 34 FROM DUAL UNION ALL
SELECT 5, 'UAE', 'AUS', DATE '2020-01-04', 14, 41 FROM DUAL;
Outputs:
TEAM_A
TEAM_B
IND
USA
AUS
IND
AUS
UAE
If you want to respect the order of the teams and get the earliest instance of a pairing of teams then:
SELECT Team_A,
Team_B
FROM (
SELECT Team_A,
Team_B,
ROW_NUMBER() OVER (
PARTITION BY LEAST(Team_A, Team_B), GREATEST(Team_A, Team_B)
ORDER BY "DATE"
) AS rn
FROM table_name
)
WHERE rn = 1;
Which outputs:
TEAM_A
TEAM_B
AUS
IND
UAE
AUS
IND
USA
db<>fiddle here

How to convert column with Rank Value into rows in oracle

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

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

Grouping SQL results by Year and count

I have a table with the below structure:
I would like to retrieve the results using sql in the below format
I am new to SQL and can't figure out how to go about it. Is this possible without using procedures? How do I go achieve this? (the actual data size is huge and I have given only a snapshot here)
Part of it is pivoting. Totals by row and column (and really, even the pivoting) should be done in your reporting application, not in SQL. If you insist on doing it in SQL, there are fancier ways, but something like the silly query below will suffice.
with test_data (city, yr, ct) as (
select 'Tokyo' , 2016, 2 from dual union all
select 'Mumbai', 2013, 3 from dual union all
select 'Mumbai', 2014, 5 from dual union all
select 'Dubai' , 2011, 5 from dual union all
select 'Dubai' , 2015, 15 from dual union all
select 'Dubai' , 2016, 8 from dual union all
select 'London', 2011, 16 from dual union all
select 'London', 2012, 22 from dual union all
select 'London', 2013, 4 from dual union all
select 'London', 2014, 24 from dual union all
select 'London', 2015, 13 from dual union all
select 'London', 2016, 5 from dual
),
test_with_totals as (
select city, yr, ct from test_data union all
select city, 9999, sum(ct) from test_data group by city union all
select 'Grand Total', yr , sum(ct) from test_data group by yr union all
select 'Grand Total', 9999, sum(ct) from test_data
)
select * from test_with_totals
pivot ( sum (ct) for yr in (2011, 2012, 2013, 2014, 2015, 2016, 9999 as "Total"))
order by "Total";
Result:
CITY 2011 2012 2013 2014 2015 2016 Total
----------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
Tokyo 2 2
Mumbai 3 5 8
Dubai 5 15 8 28
London 16 22 4 24 13 5 84
Grand Total 21 22 7 29 28 15 122