Materialized View With Monthly Substitute Variables - oracle

I am trying to reconfigure a View into a materialized view for summary roll-ups. IS there a way to implement the process so that I can gather each months parameters from the materialized view easily without having to run Substitute variables each time:
def RECORDMONTH = 201402
def BeginDate = 2/01/2014
def EndDate = 2/28/2014
def YEAR_MONTH = 2014-02
Would it be able to be designed in a way that can pull all twqeleve months even if that month has happened yet.
Really appreciate it.

I'm guessing that this is a continuation of a number of other questions that you've asked. My guess is that you just want something like this where you generate the months you want (in this case the 12 months of 2015) and use that in an outer join in your query.
WITH all_months AS
SELECT add_months( date '2015-01-01', level-1 ) start_of_month,
last_day( add_months( date '2015-01-01', level-1 )) end_of_month
FROM dual
CONNECT BY level <= 12
)
SELECT am.start_of_month,
count(*) num_rows,
<<other aggregates>>
FROM your_table yt
RIGHT OUTER JOIN all_months am
ON( yt.some_date BETWEEN am.start_of_month AND am.end_of_month )
GROUP BY am.start_of_month

Related

How to SELECT the MAX Time Difference Between Any 2 Consecutive Rows Per Value?

Just had a user answer this correctly for TSQL, but wondering how best to achieve this now in SQL Developer/PLSQL seeing as there is no DATEDIFF function.
Table I want to query on has some 'CODE' values, which can naturally have multiple primary key records ('OccsID') in a table 'Occs'. There is also a datetime column called 'CreateDT' for each OccsID.
Just want to find the maximum possible time variance between any 2 consecutive rows in 'Occs', per 'CODE'.
If you subtract the "next" date and "this" date (using the LEAD analytic function), you'll get the date difference. Then fetch the maximum difference per code. Something like this:
with diff as
(select occsid,
code,
nvl(lead(createdt) over (partition by code order by createdt), createdt) - createdt date_diff
from test
)
select code,
max(date_diff)
from diff
group by code;
Assuming that this T-SQL version works for you (from the prior question)
SELECT x.code, MAX(x.diff_sec) FROM
(
SELECT
code,
DATEDIFF(
SECOND,
CreateDT,
LEAD(CreateDT) OVER(PARTITION BY CODE ORDER BY CreateDT) --next row's createdt
) as diff_sec
FROM Occs
)x
GROUP BY x.code
The simplest option is just to subtract the two dates to get a difference in days. You can then multiply to get the difference in hours, minutes, or seconds
SELECT x.code, MAX(x.diff_day), MAX(x.diff_sec)
FROM
(
SELECT
code,
CreateDT -
LEAD(CreateDT) OVER(PARTITION BY CODE ORDER BY CreateDT) as diff_day,
24*60*60* (CreateDT -
LEAD(CreateDT) OVER(PARTITION BY CODE ORDER BY CreateDT)) as diff_sec
FROM Occs
)x
GROUP BY x.code

Loop through date range

How to loop Oracle query through the date? I have to put variable in 4 place. My query start with WITH AS, so I can't use Oracle SQL Loop through Date Range solution.
I also can't create temporary table.
Here is my attempt:
WITH d
AS (
SELECT DATE'2015-06-22' + LEVEL - 1 AS current_d
FROM dual
CONNECT BY DATE'2015-06-22' + LEVEL - 1 < DATE'2015-10-04'
),
OrderReserve
AS (
SELECT cvwarehouseid
,lproductid
,SUM(lqty) lqty
FROM ABBICS.iOrdPrdQtyDate
GROUP BY cvwarehouseid
,lproductid
)
SELECT
...
WHERE IORDREFILL.DNCONFIRMEDDELDATE < CAST(TO_CHAR(d.current_d , 'YYYYMMDD') AS NUMBER(38))
...
If I understand you correctly, you assume that you can only use 1 inline table per query. That is not true, you can use multiple inline tables and expand the existing WITH clause with another to loop through dates:
with OrderReserve as (
SELECT cvwarehouseid
,lproductid
,SUM(lqty) lqty
FROM ABBICS.iOrdPrdQtyDate
GROUP BY cvwarehouseid
,lproductid
), date_range as (
select sysdate+level
from dual
connect by level <= 30
)
select *
from OrderReserve, date_range
... -- expand with date_range as you see fit
;

Oracle Recursive Query - Dates

I've got two sets of dates being passed into a query and I would like to find all the months/years between both sets of dates.
When I try this:
WITH CTE_Dates (cte_date) AS (
SELECT cast(date '2014-01-27' as date) from dual
UNION ALL
SELECT cast(ADD_MONTHS(TRUNC(cte_date, 'MONTH'),1) as date)
FROM CTE_Dates
WHERE ( TO_DATE(ADD_MONTHS(TRUNC(cte_date, 'MONTH'), 1)) BETWEEN TO_DATE ('27-01-2014','DD-MM-YYYY') AND TO_DATE ('27-04-2014','DD-MM-YYYY'))
OR
( TO_DATE(ADD_MONTHS(TRUNC(cte_date, 'MONTH'), 1)) BETWEEN TRUNC(TO_DATE('27-11-2014','DD-MM-YYYY'), 'MONTH') AND TO_DATE ('27-01-2015','DD-MM-YYYY'))
)
SELECT * from CTE_Dates
I get:
27-JAN-14
01-FEB-14
01-MAR-14
01-APR-14
I would also want to get:
01-NOV-14
01-DEC-14
01-JAN-15
It looks like the OR portion of the WHERE clause gets ignored.
Suggestions on how to create this query?
Thanks
Cory
The problem with what you have now (aside from extra cast() and to_date() calls) is that on the fourth iteration both the conditions are false so the recursion stops; there's nothing to make it skip a bit and pick up again, otherwise it would continue forever. I don't think you can achieve both ranges within the recursion.
You can put the latest date you want inside the recursive part, and then filter the two ranges you want afterwards:
WITH CTE_Dates (cte_date) AS (
SELECT date '2014-01-27' from dual
UNION ALL
SELECT ADD_MONTHS(TRUNC(cte_date, 'MONTH'), 1)
FROM CTE_Dates
WHERE ADD_MONTHS(TRUNC(cte_date, 'MONTH'), 1) <= date '2015-01-27'
)
SELECT * from CTE_Dates
WHERE cte_date BETWEEN date '2014-01-27' AND date '2014-04-27'
OR cte_date BETWEEN date '2014-11-27' AND date '2015-01-27';
CTE_DATE
---------
27-JAN-14
01-FEB-14
01-MAR-14
01-APR-14
01-DEC-14
01-JAN-15
6 rows selected
You can replace the hard-coded values with your pairs of start and end dates. If the ranges might overlap or the second range could be (or end) before the first one, you could pick the higher date:
WHERE ADD_MONTHS(TRUNC(cte_date, 'MONTH'), 1)
<= greatest(date '2015-01-27', date '2014-04-27')
... though that only makes sense with variables, not fixed values.

Need to make a query more efficient

I have a query which I need to make more efficient.
I am breaking it down into sections to see where the efficiency floors are, I currently have a few Nested Select statements, are these a performance problem?
Here is an example of one of them:
SELECT AgreementID,
DueDate,
UpdatedAmountDue AS AmountDue,
COALESCE((SELECT SUM(UpdatedAmountDue)
FROM RepaymentBreakdown AS B
WHERE CONVERT(datetime, CONVERT(varchar, DueDate, 103), 103) <=
CONVERT(datetime, CONVERT(varchar, R.DueDate, 103), 103)
AND B.AgreementID = R.AgreementID),0) AS DueTD,
RN=ROW_NUMBER() OVER (Partition BY R.AgreementID ORDER BY DueDate)
FROM RepaymentBreakdown AS R
Is there a more clean and efficient way of getting the data of DueTD?
Basically, for each line of a repayment schedule result, I want to get:
AgreementID,
DueDate,
AmountDue,
AmountDueToDate (DueTD)
RowNumber.
The table I am querying is structured as follows:
AgreementID (int),
DueDate (datetime),
AmountDue (decimal(9,2)),
UpdatedAmountDue (decimal(9,2))*
*UpdatedAmountDue is always referenced as it is the moving figure, AmountDue is always fixed, as a reference value.
So, I think you could get performance boost just by removing convert, like this:
select
AgreementID,
DueDate,
UpdatedAmountDue as AmountDue,
(
select sum(B.UpdatedAmountDue)
from RepaymentBreakdown as B
where B.DueDate <= R.DueDate and B.AgreementID = R.AgreementID
) as UpdatedAmountDue
from RepaymentBreakdown AS R
The fastest way I know to calculate running total in SQL Server 2008 would be to use recursive CTE, see my answer here Calculate a Running Total in SqlServer. In your case the query would be smth like this:
create table #t (....., primary key (AgreementID, ord))
insert into #t (AgreementID, DueDate, UpdatedAmountDue, ord)
select AgreementID, DueDate, UpdatedAmountDue, row_number() over (partition by AgreementID, DueDate order by DueDate asc)
;with
CTE_RunningTotal
as
(
select T.ord, T.AgreementID, T.DueDate, T.UpdatedAmountDue as T.AmountDue, T.UpdatedAmountDue
from #t as T
where T.ord = 1
union all
select T.ord, T.AgreementID, T.DueDate, T.UpdatedAmountDue as T.AmountDue, T.UpdatedAmountDue + C.UpdatedAmountDue as UpdatedAmountDue
from CTE_RunningTotal as C
inner join #t as T on T.ord = C.ord + 1 and T.AgreementID = C.AgreementID
)
select AgreementID, DueDate, AmountDue, UpdatedAmountDue
from CTE_RunningTotal as C
option (maxrecursion 0)
Your conversion of the datetime to a date has several issues.
First, it is not guaranteed to always produce correct results depending on your servers language settings. If you need to do String manipulation on a datetime value always use CONVERT(,,126).
But more importantly, it prevents index usage. Instead use CAST(DueDate AS DATE) as the optimizer recognizes that conversion to be index-safe.
Afterwards you might want to add an index on AgreementId,DueDate and either INCLUDE UpdatedAmountDue or better make it clustered.
Assuming UpdatedAmountDue cannot be NULL, you can get rid of the COALESCE too, as the sum always includes the current row.

Oracle Daily count/average over a year

I'm pulling two pieces of information over a specific time period, but I would like to fetch the daily average of one tag and the daily count of another tag. I'm not sure how to do daily averages over a specific time period, can anyone provide some advice? Below were my first ideas on how to handle this however to change every date would be annoying. Any help is appreciated thanks
SELECT COUNT(distinct chargeno), to_char(chargetime, 'mmddyyyy') AS chargeend
FROM batch_index WHERE plant=1 AND chargetime>to_date('2012-06-18:00:00:00','yyyy-mm-dd:hh24:mi:ss')
AND chargetime<to_date('2012-07-19:00:00:00','yyyy-mm-dd:hh24:mi:ss')
group by chargetime;
The working version of the daily sum
SELECT to_char(bi.chargetime, 'mmddyyyy') as chargtime, SUM(cv.val)*0.0005
FROM Charge_Value cv, batch_index bi WHERE cv.ValueID =97
AND bi.chargetime<=to_date('2012-07-19','yyyy-mm-dd')
AND bi.chargeno = cv.chargeno AND bi.typ=1
group by to_char(bi.chargetime, 'mmddyyyy')
seems like in the first one you want to change the group to the day - not the time... (plus i dont think you need to specify all those 0's for seconds..)
SELECT COUNT(distinct chargeno), to_char(chargetime, 'mmddyyyy') AS chargeend
FROM batch_index WHERE plant=1 AND chargetime>to_date('2012-06-18','yyyy-mm-dd')
AND chargetime<to_date('2012-07-19','yyyy-mm-dd')
group by to_char(chargetime, 'mmddyyyy') ;
not 100% I'm following your question, but if you just want to do aggregates (sums, avg), then do just that. I threw in the rollup just in case that is what you were looking for
with fakeData as(
select trunc(level *.66667) nr
, trunc(2*level * .33478) lvl --these truncs just make the doubles ints
,trunc(sysdate+trunc(level*.263784123)) dte --note the trunc, this gets rid of the to_char to drop the time
from dual
connect by level < 600
) --the cte is just to create fake data
--below is just some aggregates that may help you
select sum(nr) daily_sum_of_nr
, avg(nr) daily_avg_of_nr
, count(distinct lvl) distinct_lvls_per_day
, count(lvl) count_of_nonNull_lvls_per_day
, dte days
from fakeData
group by rollup(dte)
--if you want the query to supply a total for the range, you may use rollup ( http://psoug.org/reference/rollup.html )

Resources