Calculating Value pro rata over months with Oracle - oracle

Say I have a table containing the term of my user subscriptions, like:
user product value start_date end_date
Scott T23K 250 15/01/2014 15/02/2015
Tiger T23K 200 05/01/2014 05/02/2015
Scott T00Z 20 10/01/2014 02/02/2015
...
And I want to build revenue report, based on my billing cycles (monthly) that would look like:
month product value
Jan/2014 T23K 275
Jan/2014 T00Z 19.1
Feb/2014 T23K 275
Feb/2014 T00Z 0.9
I guess I could query like this, though I was wondering if there wasn't a clever analytic function or something that would make it look nicer. Ideas?
I am using Oracle 11gR2

Try this, I think it match your case:
with subscriptions as
(
select 'Scott' user_, 'T23K' product , '250' value , to_date('15/01/2014','DD/MM/YYYY') start_date , to_date('15/02/2015','DD/MM/YYYY') end_date from dual
union all
select 'Tiger' user_, 'T23K' product , '200' value , to_date('05/01/2014','DD/MM/YYYY') start_date , to_date('05/02/2015','DD/MM/YYYY') end_date from dual
union all
select 'Scott' user_, 'T00Z' product , '20' value , to_date('10/01/2014','DD/MM/YYYY') start_date , to_date('02/02/2014','DD/MM/YYYY') end_date from dual
)
, allMonts as
(select add_months(to_date('01/01/2010','DD/MM/YYYY'),rownum) myMonth , lag(add_months(to_date('01/01/2010','DD/MM/YYYY'),rownum)) over (order by rownum desc) -add_months(to_date('01/01/2010','DD/MM/YYYY'),rownum) daymonth from dual CONNECT BY LEVEL <= 100
)
, detail as
(
select s.*,allMonts.myMonth, end_date-start_date duration,
case when trunc(start_date,'MON')=trunc(end_date,'MON') then (end_date-start_date)*value/( end_date-start_date)
when trunc(start_date,'MON')=allMonts.myMonth then (add_months(allMonts.myMonth,1) - start_date)*value/( end_date-start_date)
when trunc(end_date,'MON')=allMonts.myMonth then (end_date - allMonts.myMonth )*value/( end_date-start_date)
else allMonts.daymonth*value/( end_date-start_date)
end value_by_month
from subscriptions s , allMonts
where allMonts.myMonth
between trunc(s.start_date,'MON') and trunc(s.end_date,'MON')+1
)
select myMonth, product, sum(value_by_month)
from detail
group by rollup(myMonth), product

Related

Fix time issue in oracle

Table looks like below:
Expected answer below:
Write an oracle query to fix space and return hh:mm format. Consider same time in a same group and return total count
The WITH clause in the query below is just for testing; remove it, and use the actual table and column names in the main query. Note: count is a reserved keyword, so it can't be a column name. I changed it to count_ (with an underscore).
with
test_data (srt_tm, count_) as (
select '1:00' , 125 from dual union all
select '01:00' , 19000 from dual union all
select ' 01:00', 27 from dual union all
select '4:00' , 22000 from dual union all
select '04:00' , 1800 from dual union all
select ' 04:00', 15000 from dual
)
-- END OF TEST DATA; ACTUAL QUERY BEGINS **BELOW THIS LINE**
select lpad(trim(srt_tm), 5, '0') as srt_tm, sum(count_) as count_
from test_data
group by lpad(trim(srt_tm), 5, '0')
order by srt_tm
;
SRT_TM COUNT_
------ --------
01:00 19152
04:00 38800

How do I fetch data for 90 days if there is no data in 10 days using Sqplus?

I write a Query to fetch a data for 10 days from the table. If there is no data for 10 days, then i need to fetch for 50 days.But i don't know how to modify my query to fetch data for 90 days.
Query:
Select ep.NAME||'|'||s.id||'|'||s.SUBMISSION_DATE||'|'||E.VALUE
from SUMMARY_EXT e, summary s, enterprise ep
where e.id = id and e.name_res_key = 'Plan'
and s.id in (select id from summary where
trunc(start_date) > trunc(sysdate) -10 and service_name ='Dplan')
I want to modify my query as if there is a data for 10 days then it should fetch for 10 days. If there is no data then it should fetch for 90days.
Analytic functions can help return rows based on the existence of other rows. First, use CASE expressions to categorize the rows into 10 day, 50 day, or 90 day buckets. Then use analytic functions to count the number of rows in each group. Finally, select only from the relevant groups depending on those counts.
For example:
-- Return 10 days, 50 days, or 90 days of data.
--
--#3: Only return certain rows depending on the counts.
select id, start_date
from
(
--#2: Count the number of rows in each category.
select id, start_date, is_lt_10, is_lt_50, is_lt_90
,sum(is_lt_10) over () total_lt_10
,sum(is_lt_50) over () total_lt_50
,sum(is_lt_90) over () total_lt_90
from
(
--#1: Put each row into a date category.
select
id, start_date,
case when trunc(start_date) > trunc(sysdate) - 10 then 1 else 0 end is_lt_10,
case when trunc(start_date) > trunc(sysdate) - 50 then 1 else 0 end is_lt_50,
case when trunc(start_date) > trunc(sysdate) - 90 then 1 else 0 end is_lt_90
from summary
where start_date > trunc(sysdate) - 90
)
)
where
(is_lt_10 = 1 and total_lt_10 > 0) or
(is_lt_50 = 1 and total_lt_10 = 0 and total_lt_50 > 0) or
(is_lt_90 = 1 and total_lt_50 = 0 and total_lt_90 > 0);
The below views can help simulate date ranges. For complicated queries like this, it's helpful to start as simple as possible, and add all the other joins and columns later.
--Data for 10 days only.
create or replace view summary as
select 1 id, sysdate start_date from dual union all
select 2 id, sysdate-49 start_date from dual union all
select 3 id, sysdate-89 start_date from dual union all
select 4 id, sysdate-99 start_date from dual;
--Data for 50 days only.
create or replace view summary as
select 2 id, sysdate-49 start_date from dual union all
select 3 id, sysdate-89 start_date from dual union all
select 4 id, sysdate-99 start_date from dual;
--Data for 90 days only.
create or replace view summary as
select 3 id, sysdate-89 start_date from dual union all
select 4 id, sysdate-99 start_date from dual;

How to convert this code from oracle to redshift?

I am trying to implement the same in redshift and i am finding it little difficult to do that. Since redshift is in top of postgresql engine, if any one can do it in postgresql it would be really helpfull. Basically the code gets the count for previous two month at column level. If there is no count for exact previous month then it gives 0.
This is my code:
with abc(dateval,cnt) as(
select 201908, 100 from dual union
select 201907, 200 from dual union
select 201906, 300 from dual union
select 201904, 600 from dual)
select dateval, cnt,
last_value(cnt) over (order by dateval
range between interval '1' month preceding
and interval '1' month preceding ) m1,
last_value(cnt) over (order by dateval
range between interval '2' month preceding
and interval '2' month preceding ) m2
from (select to_date(dateval, 'yyyymm') dateval, cnt from abc)
I get error in over by clause. I tried to give cast('1 month' as interval) but still its failing. Can someone please help me with this windows function.
expected output:
Regards
This is how I would do it. In Redshift there's no easy way to generate sequences, do I select row_number() from an arbitrary table to create a sequence:
with abc(dateval,cnt) as(
select 201908, 100 union
select 201907, 200 union
select 201906, 300 union
select 201904, 600),
cal(date) as (
select
add_months(
'20190101'::date,
row_number() over () - 1
) as date
from <an arbitrary table to generate a sequence of rows> limit 10
),
with_lag as (
select
dateval,
cnt,
lag(cnt, 1) over (order by date) as m1,
lag(cnt, 2) over (order by date) as m2
from abc right join cal on to_date(dateval, 'YYYYMM') = date
)
select * from with_lag
where dateval is not null
order by dateval

Oracle month year temp table

I am trying to create a month, year temp table that I can relate to in calculations, however I am having some issues. I am unable to create global temp tables due to restrictions and have to rely on the following kind of query.
WITH Months AS
(
SELECT LEVEL -1 AS ID
FROM DUAL
CONNECT BY LEVEL <=264
)
(SELECT
ROWNUM AS MO_SYS_ID,
TO_CHAR(ADD_MONTHS(TO_DATE('01/01/1999', 'DD/MM/YY'), ID), 'YYYY'||'MM') AS MO_NM,
TO_CHAR(ADD_MONTHS(TO_DATE('01/01/1999', 'DD/MM/YY'), ID), 'MON') AS MO_ABBR_NM,
TO_CHAR(ADD_MONTHS(TO_DATE('01/01/1999', 'DD/MM/YY'), ID), 'MONTH') AS MO_FULL_NM,
TO_CHAR(ADD_MONTHS(TO_DATE('01/01/1999', 'DD/MM/YY'), ID), 'MM')AS MO_NBR,
TO_CHAR(ADD_MONTHS(TO_DATE('01/01/1999', 'DD/MM/YY'), ID), 'YYYY') AS YR_NBR
from Months;
What I really need to do is have this inserted into the temp table that I can recall. I do not have any fields that I can use from other tables either unfortunately. I need it to show 264 months from 1999.
Thank you
You can calculate a date column within the table expression, like this:
WITH Months AS (
SELECT LEVEL -1 AS ID, ADD_MONTHS(TO_DATE('01/01/1999', 'DD/MM/YY'), LEVEL -1) as dt
FROM DUAL
CONNECT BY LEVEL <=264
)
SELECT *
from Months
If you are attempting to create date ranges, you could do this:
WITH Months AS (
SELECT LEVEL -1 AS ID
, ADD_MONTHS(TO_DATE('01/01/1999', 'DD/MM/YY'), LEVEL -1) as start_dt
, ADD_MONTHS(TO_DATE('01/01/1999', 'DD/MM/YY'), LEVEL ) as end_dt
FROM DUAL
CONNECT BY LEVEL <=264
)
SELECT *
from yourtable t
inner join Months m on t.somecol >= m.start_dt and t.somecol < m.end_dt

Oracle sql retrive records based on maximum time

i have below data.
table A
id
1
2
3
table B
id name data1 data2 datetime
1 cash 12345.00 12/12/2012 11:10:12
1 quantity 222.12 14/12/2012 11:10:12
1 date 20/12/2012 12/12/2012 11:10:12
1 date 19/12/2012 13/12/2012 11:10:12
1 date 13/12/2012 14/12/2012 11:10:12
1 quantity 330.10 17/12/2012 11:10:12
I want to retrieve data in one row like below:
tableA.id tableB.cash tableB.date tableB.quantity
1 12345.00 13/12/2012 330.10
I want to retrieve based on max(datetime).
The data model appears to be insane-- it makes no sense to join an ORDER_ID to a CUSTOMER_ID. It makes no sense to store dates in a VARCHAR2 column. It makes no sense to have no relationship between a CUSTOMER and an ORDER. It makes no sense to have two rows in the ORDER table with the same ORDER_ID. ORDER is also a reserved word so you cannot use that as a table name. My best guess is that you want something like
select *
from customer c
join (select order_id,
rank() over (partition by order_id
order by to_date( order_time, 'YYYYMMDD HH24:MI:SS' ) desc ) rnk
from order) o on (c.customer_id=o.order_id)
where o.rnk = 1
If that is not what you want, please (as I asked a few times in the comments) post the expected output.
These are the results I get with my query and your sample data (fixing the name of the ORDER table so that it is actually valid)
SQL> ed
Wrote file afiedt.buf
1 with orders as (
2 select 1 order_id, 'iphone' order_name, '20121201 12:20:23' order_time from dual union all
3 select 1, 'iphone', '20121201 12:22:23' from dual union all
4 select 2, 'nokia', '20110101 13:20:20' from dual ),
5 customer as (
6 select 1 customer_id, 'paul' customer_name from dual union all
7 select 2, 'stuart' from dual union all
8 select 3, 'mike' from dual
9 )
10 select *
11 from customer c
12 join (select order_id,
13 rank() over (partition by order_id
14 order by to_date( order_time, 'YYYYMMDD HH24:MI:SS' ) desc ) rnk
15 from orders) o on (c.customer_id=o.order_id)
16* where o.rnk = 1
SQL> /
CUSTOMER_ID CUSTOM ORDER_ID RNK
----------- ------ ---------- ----------
1 paul 1 1
2 stuart 2 1
Try something like
SELECT *
FROM CUSTOMER c
INNER JOIN ORDER o
ON (o.CUSTOMER_ID = c.CUSTOMER_ID)
WHERE TO_DATE(o.ORDER_TIME, 'YYYYMMDD HH24:MI:SS') =
(SELECT MAX(TO_DATE(o.ORDER_TIME, 'YYYYMMDD HH24:MI:SS')) FROM ORDER)
Share and enjoy.

Resources