Calculating if an higher ranked class was offered and booked - sql

I am trying to solve following issue - and sorry if i'm drawing out too much:
A Flight Booking Platform is asking for prices on a Price Calculation Engine and its passing all necessary information to come up with prices for various options. Technically the Platform is asking for various combination of Routes (direct indirect flights, 1,2,3...stops ect.) so we will have many variants of Request ID and Route. There can be multiple requests per customer for same conditions until its booked.
The Booking Platform will - whenever it's asking for route prices - try to offer an higher class (only if available) to the customer - therefore it will do another price call within 0-5s but with a different class.
I know which values are from the higher classes e.g. U, others are just normal classes:e.g. I
I'm looking for a sql query to find out if an higher class was offered (within the next 5s) and if the customer booked the higher class - basically enhance the booking table and add "upselling offered" , "upselling realized".
"upselling offered" -> If there is an request for the same customer, origin, destination and date +-(5s) but the class is different than "yes" (higher class was available) if not "no".
"upselling upselling realized" -> If the customer asked for a lower class but then booked the upselling offer
There could be cases where no higher class was available - so in this case there would be only one class for that combination of Customer, Origin ect..
Table i'm looking for should look like:
request_id route customer origin destination req_date class price booked_request_id selected_route upselling_offered upselling_realized
124 2 c a b 2000-01-01 00:00:02.000 I 22 124 2 yes no
128 1 c a b 2000-01-05 00:00:03.000 U 24 128 1 yes yes
129 2 c a b 2000-01-05 00:00:08.000 I 23 129 2 no no
SQL for Values with the booking table:
with rr as (
select 123 as request_id, 2 as route, 'c' as customer, 'a' as origin, 'b' AS destination, convert(datetime, '2000-01-01 00:00:00') as req_date,'I' as class ,17 as price ,'normal request' as explanation union all
select 123 as request_id, 3 as route, 'c' as customer, 'a' as origin, 'b' AS destination, convert(datetime, '2000-01-01 00:00:00') as req_date,'I' as class ,20 as price ,'normal request' as explanation union all
select 124 as request_id, 1 as route, 'c' as customer, 'a' as origin, 'b' AS destination, convert(datetime, '2000-01-01 00:00:02') as req_date,'I' as class ,19 as price ,'normal request' as explanation union all
select 124 as request_id, 2 as route, 'c' as customer, 'a' as origin, 'b' AS destination, convert(datetime, '2000-01-01 00:00:02') as req_date,'I' as class ,22 as price ,'normal request' as explanation union all
select 124 as request_id, 3 as route, 'c' as customer, 'a' as origin, 'b' AS destination, convert(datetime, '2000-01-01 00:00:02') as req_date,'I' as class ,25 as price ,'normal request' as explanation union all
select 125 as request_id, 1 as route, 'c' as customer, 'a' as origin, 'b' AS destination, convert(datetime, '2000-01-01 00:00:06') as req_date,'U' as class ,26 as price ,'uselling offer' as explanation union all
select 125 as request_id, 2 as route, 'c' as customer, 'a' as origin, 'b' AS destination, convert(datetime, '2000-01-01 00:00:06') as req_date,'U' as class ,27 as price ,'uselling offer' as explanation union all
select 126 as request_id, 1 as route, 'c' as customer, 'a' as origin, 'b' AS destination, convert(datetime, '2000-01-03 00:00:03') as req_date,'I' as class ,24 as price ,'normal request' as explanation union all
select 126 as request_id, 2 as route, 'c' as customer, 'a' as origin, 'b' AS destination, convert(datetime, '2000-01-03 00:00:03') as req_date,'I' as class ,28 as price ,'normal request' as explanation union all
select 126 as request_id, 3 as route, 'c' as customer, 'a' as origin, 'b' AS destination, convert(datetime, '2000-01-03 00:00:03') as req_date,'I' as class ,23 as price ,'normal request' as explanation union all
select 127 as request_id, 1 as route, 'c' as customer, 'a' as origin, 'b' AS destination, convert(datetime, '2000-01-05 00:00:03') as req_date,'I' as class ,22 as price ,'normal request' as explanation union all
select 127 as request_id, 2 as route, 'c' as customer, 'a' as origin, 'b' AS destination, convert(datetime, '2000-01-05 00:00:03') as req_date,'I' as class ,26 as price ,'normal request' as explanation union all
select 128 as request_id, 3 as route, 'c' as customer, 'a' as origin, 'b' AS destination, convert(datetime, '2000-01-05 00:00:03') as req_date,'U' as class ,29 as price ,'uselling offer' as explanation union all
select 128 as request_id, 1 as route, 'c' as customer, 'a' as origin, 'b' AS destination, convert(datetime, '2000-01-05 00:00:03') as req_date,'U' as class ,24 as price ,'uselling offer' as explanation union all
select 129 as request_id, 2 as route, 'c' as customer, 'a' as origin, 'b' AS destination, convert(datetime, '2000-02-08 00:00:08') as req_date,'I' as class ,23 as price ,'normal request' as explanation union all
select 129 as request_id, 3 as route, 'c' as customer, 'a' as origin, 'b' AS destination, convert(datetime, '2000-02-08 00:00:08') as req_date,'I' as class ,26 as price ,'normal request' as explanation
),
bookings as (
select 124 as booked_request_id, 2 as selected_route union all
select 128 as booked_request_id, 1 as selected_route union all
select 129 as booked_request_id, 2 as selected_route
)
--select req_date, class, lead(req_date) over (partition by customer,origin, destination, class order by req_date )
--from rr left join bookings
-- on rr.request_id = bookings.booked_request_id
-- and rr.route = bookings.selected_route
--order by class,req_date
select req_date, request_id, route, class,
case when class = 'I' then
case when
lead(req_date) over (partition by customer,origin, destination, class, req_date order by req_date ) <= dateadd(second, 5, req_date)
and lead(class) over (partition by customer,origin, destination, class, req_date order by req_date ) <> class
then 'Yes' else 'No' end
when class = 'U' then
case when
lag(req_date) over (partition by customer,origin, destination, class, req_date order by req_date ) >= dateadd(second, -5, req_date)
and lag(class) over (partition by customer,origin, destination, class, req_date order by req_date ) <> class
then 'Yes' else 'No' end
end as upselling_offered
from rr left join bookings
on rr.request_id = bookings.booked_request_id
and rr.route = bookings.selected_route
order by req_date
Unfortunately my query doies not give the desired result - any idea what i'm missing?

Why not do a select that looks for the same customer, for the same route,
WHERE customer = {customer}
AND price > {previous booked price}
AND class > {previous booked class}
AND req_date > {previous_req_date}
AND req_date <= {previous_req_date + 5 secs}
AND upselling_offered = yes
AND upselling_realized = yes
The items in brackets are values you would code into your query request.
Also I recommend an index on customer, req_date, upselling_offered and upselling_realized.
If this does not answer your question, I may not be fully understanding what you are wanting to achieve.

meanwhile i have solved it - thanks. My issue was that in the window function i grouped by class which gave me the wrong window to look for the lead/lag value...
Here is the solution:
with rr as (
select 123 as request_id, 2 as route, 'c' as customer, 'a' as origin, 'b' AS destination, convert(datetime, '2000-01-01 00:00:00') as req_date,'I' as class ,17 as price ,'normal request' as explanation union all
select 123 as request_id, 3 as route, 'c' as customer, 'a' as origin, 'b' AS destination, convert(datetime, '2000-01-01 00:00:00') as req_date,'I' as class ,20 as price ,'normal request' as explanation union all
select 124 as request_id, 1 as route, 'c' as customer, 'a' as origin, 'b' AS destination, convert(datetime, '2000-01-01 00:00:02') as req_date,'I' as class ,19 as price ,'normal request' as explanation union all
select 124 as request_id, 2 as route, 'c' as customer, 'a' as origin, 'b' AS destination, convert(datetime, '2000-01-01 00:00:02') as req_date,'I' as class ,22 as price ,'normal request' as explanation union all
select 124 as request_id, 3 as route, 'c' as customer, 'a' as origin, 'b' AS destination, convert(datetime, '2000-01-01 00:00:02') as req_date,'I' as class ,25 as price ,'normal request' as explanation union all
select 125 as request_id, 1 as route, 'c' as customer, 'a' as origin, 'b' AS destination, convert(datetime, '2000-01-01 00:00:06') as req_date,'U' as class ,26 as price ,'uselling offer' as explanation union all
select 125 as request_id, 2 as route, 'c' as customer, 'a' as origin, 'b' AS destination, convert(datetime, '2000-01-01 00:00:06') as req_date,'U' as class ,27 as price ,'uselling offer' as explanation union all
select 126 as request_id, 1 as route, 'c' as customer, 'a' as origin, 'b' AS destination, convert(datetime, '2000-01-03 00:00:03') as req_date,'I' as class ,24 as price ,'normal request' as explanation union all
select 126 as request_id, 2 as route, 'c' as customer, 'a' as origin, 'b' AS destination, convert(datetime, '2000-01-03 00:00:03') as req_date,'I' as class ,28 as price ,'normal request' as explanation union all
select 126 as request_id, 3 as route, 'c' as customer, 'a' as origin, 'b' AS destination, convert(datetime, '2000-01-03 00:00:03') as req_date,'I' as class ,23 as price ,'normal request' as explanation union all
select 127 as request_id, 1 as route, 'c' as customer, 'a' as origin, 'b' AS destination, convert(datetime, '2000-01-05 00:00:03') as req_date,'I' as class ,22 as price ,'normal request' as explanation union all
select 127 as request_id, 2 as route, 'c' as customer, 'a' as origin, 'b' AS destination, convert(datetime, '2000-01-05 00:00:03') as req_date,'I' as class ,26 as price ,'normal request' as explanation union all
select 128 as request_id, 3 as route, 'c' as customer, 'a' as origin, 'b' AS destination, convert(datetime, '2000-01-05 00:00:03') as req_date,'U' as class ,29 as price ,'uselling offer' as explanation union all
select 128 as request_id, 1 as route, 'c' as customer, 'a' as origin, 'b' AS destination, convert(datetime, '2000-01-05 00:00:03') as req_date,'U' as class ,24 as price ,'uselling offer' as explanation union all
select 129 as request_id, 2 as route, 'c' as customer, 'a' as origin, 'b' AS destination, convert(datetime, '2000-02-08 00:00:08') as req_date,'I' as class ,23 as price ,'normal request' as explanation union all
select 129 as request_id, 3 as route, 'c' as customer, 'a' as origin, 'b' AS destination, convert(datetime, '2000-02-08 00:00:08') as req_date,'I' as class ,26 as price ,'normal request' as explanation
),
bookings as (
select 124 as booked_request_id, 2 as selected_route union all
select 128 as booked_request_id, 1 as selected_route union all
select 129 as booked_request_id, 2 as selected_route
),
upsell_table as (
select request_id, max(upselling_offered) as upselling, class from
( select req_date, request_id, route, class,
--lead(req_date) over (partition by customer,origin, destination order by req_date asc) AS lead_date,
--lead(class) over (partition by customer,origin, destination order by req_date asc) AS lead_class,
case when class = 'I' then
case when
lead(req_date) over (partition by customer,origin, destination order by req_date asc) <= dateadd(second, 5, req_date)
and lead(class) over (partition by customer,origin, destination order by req_date asc) <> class
then 1 else 0 end
when class = 'U' then
case when
lag(req_date) over (partition by customer, origin, destination order by req_date ) >= dateadd(second, -5, req_date)
and lag(class) over (partition by customer, origin, destination order by req_date ) <> class
then 1 else 0 end
end as upselling_offered
from rr left join bookings
on rr.request_id = bookings.booked_request_id
and rr.route = bookings.selected_route
) T1
group by request_id, class
)
select booked_request_id, selected_route,
case when upselling = 1 then 'yes' else 'no' end as 'upselling_offered'
,case when upselling = 1 and T1.class = 'U' then 'yes' else 'no' end as 'upselling_successfull'
from upsell_table T1 join bookings
on request_id = bookings.booked_request_id

Related

Rolling up monthly data to quarterly

I have monthly data for each company and it's associated MRR (Monthly Revenue) as of the month end date. I am trying to build a quarterly report which shows the progression of the company within the quarter. Following is the reproducible sample dataset with quarter end dates:
WITH
quarter_end_date AS
(
SELECT '2021-04-30' q_end_date
UNION ALL
SELECT '2021-07-31' q_end_date
UNION ALL
SELECT '2021-10-31' q_end_date
UNION ALL
SELECT '2022-01-31' q_end_date
),
data AS
(
SELECT '2020-05-31' as_of_date, 'A' company_id, '100' mrr,'new' category
UNION ALL
SELECT '2020-06-30' as_of_date, 'A' company_id, '100' mrr,'no change' category
UNION ALL
SELECT '2020-07-31' as_of_date, 'A' company_id, '100' mrr,'no change' category
UNION ALL
SELECT '2020-08-31' as_of_date, 'A' company_id, '100' mrr,'no change' category
UNION ALL
SELECT '2020-09-30' as_of_date, 'A' company_id, '0' mrr,'churn' category
UNION ALL
SELECT '2020-10-31' as_of_date, 'A' company_id, '100' mrr,'new' category
UNION ALL
SELECT '2020-11-30' as_of_date, 'A' company_id, '100' mrr,'no change' category
UNION ALL
SELECT '2020-12-31' as_of_date, 'A' company_id, '0' mrr,'churn' category
UNION ALL
SELECT '2021-01-31' as_of_date, 'A' company_id, '0' mrr,'new' category
UNION ALL
SELECT '2021-02-28' as_of_date, 'A' company_id, '100' mrr,'no change' category
UNION ALL
SELECT '2021-03-31' as_of_date, 'A' company_id, '100' mrr,'no change' category
UNION ALL
SELECT '2021-04-30' as_of_date, 'A' company_id, '120' mrr,'expansion' category
UNION ALL
SELECT '2021-05-31' as_of_date, 'A' company_id, '90' mrr,'contraction' category
UNION ALL
SELECT '2021-06-30' as_of_date, 'A' company_id, '70' mrr,'contraction' category
UNION ALL
SELECT '2021-07-31' as_of_date, 'A' company_id, '100' mrr,'expansion' category
UNION ALL
SELECT '2021-08-31' as_of_date, 'A' company_id, '100' mrr,'no change' category
UNION ALL
SELECT '2021-09-30' as_of_date, 'A' company_id, '120' mrr,'expansion' category
UNION ALL
SELECT '2021-10-31' as_of_date, 'A' company_id, '90' mrr,'contraction' category
UNION ALL
SELECT '2021-11-30' as_of_date, 'A' company_id, '0' mrr, 'churn' category
),
The MRR dataset (above CTE data) doesn’t store company info once it churns (stops paying subscription or cancels the service). The approach I'm using is capable of capturing any churn which happens in the start of the quarter or the end of quarter but it doesn’t if churn happens within the quarter. My SQL:
step_1
AS(
SELECT *,
LAG(mrr,1) OVER (PARTITION BY company_id ORDER BY as_of_date) prev_m1_mrr,
LAG(mrr,2) OVER (PARTITION BY company_id ORDER BY as_of_date) prev_m2_mrr,
LAG(mrr,3) OVER (PARTITION BY company_id ORDER BY as_of_date) quarter_start_mrr,
FROM data
)
SELECT *
FROM step_1
CROSS JOIN quarter_end_date q
WHERE q.q_end_date = step_1.as_of_date
ORDER BY q.q_end_date DESC
Output:
q_end_date
Company_id
mrr
category
prev_m1_mrr
prev_m2_mrr
quarter_start_mrr
2021-10-31
A
90
contraction
120
100
100
2021-07-31
A
100
expansion
70
90
120
2021-04-30
A
120
expansion
100
100
null
Expected Output:
q_end_date
Company_id
mrr
category
prev_m1_mrr
prev_m2_mrr
quarter_start_mrr
2022-01-31
A
null
churn
0
0
90
2021-10-31
A
90
contraction
120
100
100
2021-07-31
A
100
expansion
70
90
120
2021-04-30
A
120
expansion
100
100
null
I am assuming that data of all companies are in the same table, otherwise the query will be straightforward. I added a few values with a second company ‘B’. The query works, but it will give ‘Churn’ as the category whenever data is not present in the entire quarter for that company.That also includes the period when the company did not even start using the service. That can be filtered out later with simple queries based on start date if needed.
Second thing is I added another CTE for the list of companies.
WITH
quarter_end_date AS
(
SELECT '2021-04-30' q_end_date
UNION ALL
SELECT '2021-07-31' q_end_date
UNION ALL
SELECT '2021-10-31' q_end_date
UNION ALL
SELECT '2022-01-31' q_end_date
),
companies as
(SELECT 'A'as company_id
union all
SELECT 'B'as company_id
),
data AS
(
SELECT '2021-02-28' as_of_date, 'A' company_id, '100' mrr,'new' category
UNION ALL
SELECT '2021-03-31' as_of_date, 'A' company_id, '100' mrr,'no change' category
UNION ALL
SELECT '2021-04-30' as_of_date, 'A' company_id, '120' mrr,'expansion' category
UNION ALL
SELECT '2021-05-31' as_of_date, 'A' company_id, '90' mrr,'contraction' category
UNION ALL
SELECT '2021-06-30' as_of_date, 'A' company_id, '70' mrr,'contraction' category
UNION ALL
SELECT '2021-07-31' as_of_date, 'A' company_id, '100' mrr,'expansion' category
UNION ALL
SELECT '2021-08-31' as_of_date, 'A' company_id, '100' mrr,'no change' category
UNION ALL
SELECT '2021-09-30' as_of_date, 'A' company_id, '120' mrr,'expansion' category
UNION ALL
SELECT '2021-10-31' as_of_date, 'A' company_id, '90' mrr,'contraction' category
UNION ALL
SELECT '2021-11-30' as_of_date, 'A' company_id, '0' mrr, 'churn' category
union all
SELECT '2021-10-31' as_of_date, 'B' company_id, '100' mrr,'no change' category
UNION ALL
SELECT '2021-11-30' as_of_date, 'B' company_id, '120' mrr,'expansion' category
UNION ALL
SELECT '2021-12-31' as_of_date, 'B' company_id, '90' mrr,'contraction' category
UNION ALL
SELECT '2022-01-31' as_of_date, 'B' company_id, '100' mrr, 'expansion' category
),
step_1
AS(
SELECT *,
LAG(mrr,1) OVER (PARTITION BY company_id ORDER BY as_of_date) prev_m1_mrr,
LAG(mrr,2) OVER (PARTITION BY company_id ORDER BY as_of_date) prev_m2_mrr,
LAG(mrr,3) OVER (PARTITION BY company_id ORDER BY as_of_date) quarter_start_mrr,
FROM data
)
SELECT q_end_date,c.company_id,
mrr,IFNULL(category,'churn') as category,IFNULL(prev_m1_mrr,'0') as prev_m1_mrr, IFNULL(prev_m2_mrr,'0') as prev_m2_mrr,IFNULL(quarter_start_mrr,lead(mrr) over (partition by c.company_id order by q_end_date desc)) as quarter_start_mrr
FROM companies c cross join quarter_end_date q left join step_1 s on q.q_end_date = s.as_of_date and c.company_id=s.company_id
ORDER BY company_id,q.q_end_date DESC
Output:

Selecting records with max date and a date preceding the max date in the same table

I have the following table
R_ID DATE Col_A Col_B Col_C
158 20161008 01 01 99
158 20161012 01 01 99
158 20161019 01 02 10
158 20161022 99 01 10
I want to select such that I get the following result
R_ID DATE Col_A Col_B Col_C
158 20161008 01 01 99
158 20161022 99 01 10
The logic here being
1. 'select max date' for record with Col_C = '10' for a particular R_ID and
2. When Col_A or Col_B = '01' and Col_C <> '10' select the minimum Date which is < Max_date used in 1st condition
I'm using union condition like below
Select * from tbl1 T
where
T.Col_C = '10' and
T.DATE = (select max(T2.DATE) from tbl1 T2
where
T2.Col_C = '10' and
T3.R_ID = T.R_ID
)
union
Select * from tbl1 K
where
(K.Col_A = '01' or K.Col_B = '01') and
K.Col_C <> '10' and
K.DATE = (select min(K2.DATE) from tbl1 K2 where
(K2.Col_A = '01' or K2.Col_B = '01') and
K2.Col_C <> '10' and
K2.R_ID = K.R_ID
) and
--K.DATE < T.DATE-- How do I use this condition within union?
I want to be able to use the condition within the comment but am unable to figure the right syntax
I have updated the condition
Here is a brute force method:
WITH aset
AS (SELECT 158 rid, DATE '2016-10-08' d, '01' col_a, '01' col_b, '99' col_c FROM DUAL
UNION ALL
SELECT 158, DATE '2016-10-12', '01', '01', '99' FROM DUAL
UNION ALL
SELECT 158, DATE '2016-10-19', '01', '02', '10' FROM DUAL
UNION ALL
SELECT 158, DATE '2016-10-22', '99', '01', '10' FROM DUAL)
, bset
AS ( SELECT rid, MAX (d) maxd_10
FROM aset
WHERE col_c = '10'
GROUP BY rid)
, cset
AS ( SELECT aset.rid, MIN (d) mind
FROM aset INNER JOIN bset ON aset.rid = bset.rid
WHERE aset.col_c <> '10'
GROUP BY aset.rid)
SELECT aset.*
FROM aset
INNER JOIN bset
ON aset.rid = bset.rid
AND aset.d = bset.maxd_10
AND col_c = '10'
UNION ALL
SELECT aset.*
FROM aset
INNER JOIN cset
ON aset.rid = cset.rid
AND aset.d = cset.mind
AND aset.col_c <> '10';
Results in:
RID D COL_A COL_B COL_C
158 10/22/2016 99 01 10
158 10/08/2016 01 01 99
Analytic functions can be helpful for this.
with dataset as (select 158 r_id, date '2016-10-08' value_date, '01' col_a, '01' col_b, '99' col_c from dual
union all
select 158, date '2016-10-12', '01', '01', '99' from dual
union all
select 158, date '2016-10-19', '01', '02', '10' from dual
union all
select 158, date '2016-10-22', '99', '01', '10' from dual)
select d3.*
from (select d2.*,
min(case when d2.col_c <> '10' and
'01' in (d2.col_a, d2.col_b) and
d2.value_date < d2.ceiling_date then d2.value_date
else null
end) over (partition by d2.r_id
order by d2.value_date
rows between unbounded preceding and unbounded following) smallest_date
from (select d.*,
max(case when d.col_c = '10' then d.value_date else null end)
over (partition by d.r_id
order by d.value_date
rows between unbounded preceding and unbounded following) ceiling_date
from dataset d) d2) d3
where d3.ceiling_date is not null -- ceiling_date contains null if we were unable to find row with '10' in COL_C for specified r_id - remove such rows from result set
and (d3.value_date = d3.ceiling_date -- here we presume that combination of r_id + value_date gives us unique row
or d3.value_date = d3.smallest_date); -- otherwise we should add some column(s) that help us decide what rows do we need (row_number() with custom order by clause may be helpful)

SQL : Pivot way to query data

I have a table as shown below. I want to query and show row data as columns
Customer MetricName MetricValue Date
A Upload 2 10-AUG-2007
A Download 2 10-AUG-2007
A Storage 100 10-AUG-2007
A Storage 110 11-AUG-2007
B Storage 200 11-AUG-2007
A Upload 2 12-AUG-2007
A Download 2 12-AUG-2007
B Upload 2 10-AUG-2007
B Download 2 10-AUG-2007
Usage Last Week
Download - sum of all downloads in a week
Storage - Highest Value in the week
Customer Download Upload Storage
A 4 4 110
B 2 2 200
How to achieve this using Pivot or other method
Something like this... PIVOT requires the same aggregate function to be applied all to columns; the old "manual" way of pivoting, using case expressions, is more flexible (as shown below). I wasn't sure what you meant by "week" - I just put that in a WHERE clause. Also, don't use reserved words like DATE for column (or table) names, you can't do it directly and you shouldn't do it the only possible way (using double quotes) - it's a very poor practice. I changed the column name Date to dt.
with
input_data ( customer, metricname, metricvalue, dt ) AS (
select 'A', 'Upload' , 2 , to_date('10-AUG-2007', 'dd-MON-yyyy') from dual union all
select 'A', 'Download', 2 , to_date('10-AUG-2007', 'dd-MON-yyyy') from dual union all
select 'A', 'Storage' , 100 , to_date('10-AUG-2007', 'dd-MON-yyyy') from dual union all
select 'A', 'Storage' , 110 , to_date('11-AUG-2007', 'dd-MON-yyyy') from dual union all
select 'B', 'Storage' , 200 , to_date('11-AUG-2007', 'dd-MON-yyyy') from dual union all
select 'A', 'Upload' , 2 , to_date('12-AUG-2007', 'dd-MON-yyyy') from dual union all
select 'A', 'Download', 2 , to_date('12-AUG-2007', 'dd-MON-yyyy') from dual union all
select 'B', 'Upload' , 2 , to_date('10-AUG-2007', 'dd-MON-yyyy') from dual union all
select 'B', 'Download', 2 , to_date('10-AUG-2007', 'dd-MON-yyyy') from dual
)
select customer,
sum( case when metricname = 'Upload' then metricvalue end) as upload,
sum( case when metricname = 'Download' then metricvalue end) as download,
max( case when metricname = 'Storage' then metricvalue end) as storage
from input_data
where dt between to_date('09-AUG-2007', 'dd-MON-yyyy') and
to_date('15-AUG-2007', 'dd-MON-yyyy')
group by customer
order by customer
;
CUSTOMER UPLOAD DOWNLOAD STORAGE
-------- ---------- ---------- ----------
A 4 4 110
B 2 2 200

SQL how to count how many months have passed since certain status

I have a table with project, period and status with their periodic values. My goal is to show how many months have passed since certain project was last in 'Approved status' (or show what period this status was changed) in the period_leaving_approved_status column. for example, for periods 201005 to 201008 - i would need to show '201005' values, but for periods 201011-201012 - '201011' values. I managed to mark the line where status changes, but i don't know how to apply the condition for the following rows. My example query :
with subq as (
select 123 as project, 201002 as period, 'Approved' as status from dual
union all
select 123 as project, 201003 as period, 'Approved' as status from dual
union all
select 123 as project, 201004 as period, 'Approved' as status from dual
union all
select 123 as project, 201005 as period, 'Pending Close' as status from dual
union all
select 123 as project, 201006 as period, 'Pending Close' as status from dual
union all
select 123 as project, 201007 as period, 'Closed' as status from dual
union all
select 123 as project, 201008 as period, 'Closed' as status from dual
union all
select 123 as project, 201009 as period, 'Approved' as status from dual
union all
select 123 as project, 201010 as period, 'Approved' as status from dual
union all
select 123 as project, 201011 as period, 'Closed' as status from dual
union all
select 123 as project, 201012 as period, 'Closed' as status from dual
union all
select 123 as project, 201101 as period, 'Approved' as status from dual
union all
select 123 as project, 201102 as period, 'Approved' as status from dual
union all
select 123 as project, 201112 as period, 'Approved' as status from dual
union all
select 123 as project, 201301 as period, 'Pending Close' as status from dual
union all
select 123 as project, 201302 as period, 'Closed' as status from dual
union all
select 123 as project, 201203 as period, 'Closed' as status from dual
)
select project,
period,
status,
case when lag(status, 1, null) OVER (ORDER BY period)='Approved'
AND lag(status, 1, null) OVER (ORDER BY period) NOT IN(status) then period end as period_leaving_approved_status
from subq
I would approach this using aggregation:
select project,
max(case when status = 'Approved' then period end) as ApprovedPeriod
from subq;
If you want the time span then something like this:
select project,
months_between(max(case when status = 'Approved' then period end),
sysdate) as monthsSinceApproved,
max(case when status = 'Approved' then period end) as ApprovedPeriod
from subq
group by project;
EDIT:
If you want this information cumulatively on all rows, then use window functions:
select s.*,
max(case when status = 'Approved' then period end) over
(partition b project order by period) as LastApprovedPeriod
from subq s;

How to calculate price change over 3 years in SQL query

I need to calculate the price change of an item (both in cost and % change) over the last three years.
The table has four fields:
SKU_no, Date_updated, Price, Active_flag
When the Active_flag field is A, the item is active, when I it is inactive. Some items haven't changed prices in years so they won't have three years of entries with an inactive flag.
Sample table
SKU_NO Update_date Price Active_flag
30 1/1/1999 40.8 I
33 1/1/2014 70.59 A
33 1/1/2013 67.23 I
33 1/1/2012 60.03 I
33 1/1/2011 55.08 I
33 1/1/2010 55.08 I
34 1/1/2009 51 A
36 1/1/2014 70.59 A
36 1/1/2013 67.23 I
36 1/1/2012 60.03 I
38 1/1/2002 43.32 A
38 1/1/2001 43.32 I
38 4/8/2000 43.32 I
38 1/1/1999 43.32 I
39 1/1/2014 73.08 A
39 1/1/2013 69.6 I
39 1/1/2012 62.13 I
39 1/1/2011 57 I
39 1/1/2010 57 I
39 1/1/2009 52.8 I
This is the first query I wrote. I'm not too familiar with complex calculations
select
s.VENDOR,
s.FISCAL_YEAR,
s.FISCAL_MONTH_NO,
s.FISCAL_YEAR||'_'||FISCAL_MONTH_NO as PERIOD,
CASE WHEN S.COST_USED_FLAG IN ('CONTRACT') THEN 'CONTRACT' ELSE 'NON-CONTRACT' END AS CONTRACT_TYPE,
CASE WHEN ((s.FISCAL_YEAR = 2014 AND FISCAL_MONTH_NO <=9) OR (FISCAL_YEAR = 2013 AND FISCAL_MONTH_NO >=10)) THEN 'CP_1'
WHEN ((s.FISCAL_YEAR = 2013 AND FISCAL_MONTH_NO <= 9) OR (FISCAL_YEAR = 2012 AND FISCAL_MONTH_NO >=10)) THEN 'CP_2'
WHEN ((s.FISCAL_YEAR = 2012 AND FISCAL_MONTH_NO <= 9) OR (FISCAL_YEAR = 2011 AND FISCAL_MONTH_NO >=10)) THEN 'CP_3'
ELSE 'NULL' END CAGR_PERIODS,
CASE WHEN s.MARKET IN ('PO', 'SC', 'OC') THEN 'PC' ELSE 'EC' END AS MARKET_TYPE,
s.MARKET,
s.COST_PLUS_FLAG,
s.COST_USED_FLAG,
LPAD(S.PC_ITEM_NO,6,'0') AS NEW_ITEM_NO,
s.PC_ITEM_NO,
i.ITEM_NO,
i.VEND_CAT_NUM,
i.DESCRIPTION,
s.PC_PROD_CAT,
s.PC_PROD_SUBCAT,
i.SELL_UOM,
i.QTY_PER_SELL_UOM,
i.PRIMARY_UOM,
i.HEAD_CONV_FACT,
SUM(s.QTY_EACH) AS QUANTITY_SOLD,
SUM(s.EXT_GROSS_COGS) AS TOTAL_COGS,
SUM(s.EXT_GROSS_COGS)/ SUM(s.QTY_EACH) as NET_SALES,
SUM(s.EXT_SALES)/ SUM(s.QTY_EACH) as ASP,
SUM(s.EXT_SALES) AS TOTAL_SALES,
SUM(S.EXT_SALES) - SUM(S.EXT_GROSS_COGS) as GROSS_PROFIT
from SIXSIGMA.CIA_ALL_SALES_TREND_DATA s
INNER JOIN MGMSH.ITEM i
ON S.PC_ITEM_NO = I.ITEM_NO
WHERE S.VENDOR = 'BD' AND
(S.EXT_SALES IS NOT NULL AND S.FISCAL_YEAR IN ('2013','2012','2011'))
GROUP BY
s.VENDOR,
s.FISCAL_YEAR,
s.FISCAL_MONTH_NO,
s.FISCAL_YEAR||'_'||FISCAL_MONTH_NO,
CASE WHEN s.MARKET IN ('PO', 'SC', 'OC') THEN 'PC' ELSE 'EC' END,
CASE WHEN S.COST_USED_FLAG IN ('CONTRACT') THEN 'CONTRACT' ELSE 'NON-CONTRACT' END,
CASE WHEN ((s.FISCAL_YEAR = 2014 AND FISCAL_MONTH_NO <=9) OR (FISCAL_YEAR = 2013 AND FISCAL_MONTH_NO >=10)) THEN 'CP_1'
WHEN ((s.FISCAL_YEAR = 2013 AND FISCAL_MONTH_NO <= 9) OR (FISCAL_YEAR = 2012 AND FISCAL_MONTH_NO >=10)) THEN 'CP_2'
WHEN ((s.FISCAL_YEAR = 2012 AND FISCAL_MONTH_NO <= 9) OR (FISCAL_YEAR = 2011 AND FISCAL_MONTH_NO >=10)) THEN 'CP_3'
ELSE 'NULL' END,
s.MARKET,
s.COST_USED_FLAG,
s.COST_PLUS_FLAG,
s.PC_ITEM_NO,
s.PC_PROD_CAT,
i.SELL_UOM,
i.QTY_PER_SELL_UOM,
i.PRIMARY_UOM,
i.HEAD_CONV_FACT,
i.DESCRIPTION,
i.VEND_CAT_NUM,
s.PC_PROD_SUBCAT,
i.ITEM_NO
ORDER BY s.PC_ITEM_NO,s.FISCAL_YEAR, s.FISCAL_MONTH_NO
There are several ways to approach this, but I would recommend a windowing function such as LAG or LEAD. With these functions, you can reference neighboring rows. For example:
lead(column, offset, default) over (partition by some_column order by column)
And in the example below:
lead(price, 1, price) over (partition by sku_no order by update_date desc)
Here is a working example with sample data:
with sample_data as (
select '30' sku_no, to_date('1/1/1999','DD/MM/YYYY') update_date, 40.8 price, 'I' active_flag from dual union all
select '33', to_date('1/1/2014','DD/MM/YYYY'), 70.59, 'A' from dual union all
select '33', to_date('1/1/2013','DD/MM/YYYY'), 67.23, 'I' from dual union all
select '33', to_date('1/1/2012','DD/MM/YYYY'), 60.03, 'I' from dual union all
select '33', to_date('1/1/2011','DD/MM/YYYY'), 55.08, 'I' from dual union all
select '33', to_date('1/1/2010','DD/MM/YYYY'), 55.08, 'I' from dual union all
select '34', to_date('1/1/2009','DD/MM/YYYY'), 51 , 'A' from dual union all
select '36', to_date('1/1/2014','DD/MM/YYYY'), 70.59, 'A' from dual union all
select '36', to_date('1/1/2013','DD/MM/YYYY'), 67.23, 'I' from dual union all
select '36', to_date('1/1/2012','DD/MM/YYYY'), 60.03, 'I' from dual union all
select '38', to_date('1/1/2002','DD/MM/YYYY'), 43.32, 'A' from dual union all
select '38', to_date('1/1/2001','DD/MM/YYYY'), 43.32, 'I' from dual union all
select '38', to_date('4/8/2000','DD/MM/YYYY'), 43.32, 'I' from dual union all
select '38', to_date('1/1/1999','DD/MM/YYYY'), 43.32, 'I' from dual union all
select '39', to_date('1/1/2014','DD/MM/YYYY'), 73.08, 'A' from dual union all
select '39', to_date('1/1/2013','DD/MM/YYYY'), 69.6 , 'I' from dual union all
select '39', to_date('1/1/2012','DD/MM/YYYY'), 62.13, 'I' from dual union all
select '39', to_date('1/1/2011','DD/MM/YYYY'), 57 , 'I' from dual union all
select '39', to_date('1/1/2010','DD/MM/YYYY'), 57 , 'I' from dual union all
select '39', to_date('1/1/2009','DD/MM/YYYY'), 52.8 , 'I' from dual)
select
sku_no,
update_date,
price,
lead(price,1, price) over (partition by sku_no order by update_date desc) prior_price, -- Showing the offset
price - lead(price,1, price) over (partition by sku_no order by update_date desc) price_difference, -- Calculate the difference
round((price - lead(price,1, price) over (partition by sku_no order by update_date desc)) * 100 /price, 2) percent_change -- Calculate the percentage
from sample_data
where update_date >= add_months(trunc(sysdate,'YYYY'),-36); -- You said in the last three years
You can also use LAG with a different order by sort. If you want to calculate the difference from three years prior, I would suggest using the KEEP function.