How to group data results by month - oracle

I have a script that I wrote that will tell me what our "On Time Delivery" or "OTD" is based on the current month. Essentially it tells us instantly if we have shipped our materials before or on our promise date.
What I am trying to do now is create a new script that will tie this into our GLPERIODS table so that I can show what the "OTD" is for each month of the year at a glance. When I run this new I get the correct period (Jan-Dec) but my OTD calculation is not grouped by period. Instead they are all the same and I think it's an average of all the data in our system.
I will post the script here as well as the results. (edit: I guess I can't post the results as it won't let me post an image) Any help would be greatly appreciated. Thank you and BTW, I am not a programmer, just someone who works on this stuff occasionally to get the data we are after.
Select "month",
OTD
From (Select Round((1 - ("Late" / "All")), 3) As OTD
From (Select Round(Count(V_RELEASES_COMB.ACTUAL_SHIPDATE), 2) As "Late"
From V_RELEASES_COMB,
GLPERIODS
Where To_Char(V_RELEASES_COMB.ACTUAL_SHIPDATE, 'MM/YYYY') =
To_Char(GLPERIODS.START_DATE, 'MM/YYYY') And
V_RELEASES_COMB.ACTUAL_SHIPDATE > V_RELEASES_COMB.PROMISE_DATE And
V_RELEASES_COMB.ITEMNO Is Not Null And V_RELEASES_COMB.CUMM_SHIPPED > 0),
(Select Case
When Round(Count(V_RELEASES_COMB.ACTUAL_SHIPDATE), 2) = 0 Then 1
Else Round(Count(V_RELEASES_COMB.ACTUAL_SHIPDATE), 2) End As "All"
From V_RELEASES_COMB,
GLPERIODS
Where To_Char(V_RELEASES_COMB.ACTUAL_SHIPDATE, 'MM/YYYY') =
To_Char(GLPERIODS.START_DATE, 'MM/YYYY') And V_RELEASES_COMB.ITEMNO Is Not
Null And V_RELEASES_COMB.CUMM_SHIPPED > 0)),
(Select To_Char(GLPERIODS.START_DATE, 'MONTH') As "month"
From GLPERIODS
Where Extract(Year From GLPERIODS.START_DATE) = Extract(Year From SysDate))
Ok, thank you for the help! Here is the edited script with your input added:
Select "month",
OTD
From (Select trunc(g.start_date, 'Month') mth,
round ( 1 - ( count(
case when v.ACTUAL_SHIPDATE > v.PROMISE_DATE And v.ITEMNO Is Not Null
And v.CUMM_SHIPPED > 0 then V.ACTUAL_SHIPDATE end)
/ greatest(Count(
case when v.ITEMNO Is Not Null And v.CUMM_SHIPPED > 0 then V.ACTUAL_SHIPDATE end
), 1) ) , 3) OTD
from glperiods g
left join v_releases_comb v
on trunc(v.ACTUAL_SHIPDATE, 'Month') = trunc(g.start_date, 'Month')
group by trunc(g.start_date, 'Month')),
(Select To_Char(GLPERIODS.START_DATE, 'MONTH') As "month"
From GLPERIODS
Where Extract(Year From GLPERIODS.START_DATE) = Extract(Year From SysDate))
The results look promising but here is what is happening:
Month OTD
January .956
January .875
January .359
January 1
January 1
January 1
February .978
February .562
February .875
February 1
February 1
etc.
this continues for all months.
The above is in a table format but I have no idea how to post a table on this site. I wish I could just upload a pic but it won't let me.

This query should do the job:
SQLFiddle
select to_char(mth, 'Month') "Month", OTD
from (Select trunc(g.start_date, 'Month') mth,
round ( 1 - ( count(
case when v.ACTUAL_SHIPDATE > v.PROMISE_DATE And v.ITEMNO Is Not Null
And v.CUMM_SHIPPED > 0 then V.ACTUAL_SHIPDATE end)
/ greatest(Count(
case when v.ITEMNO Is Not Null And v.CUMM_SHIPPED > 0 then V.ACTUAL_SHIPDATE end
), 1) ) , 3) OTD
from glperiods g
left join v_releases_comb v
on trunc(v.ACTUAL_SHIPDATE, 'Month') = trunc(g.start_date, 'Month')
Where Extract(Year From g.START_DATE) = Extract(Year From SysDate)
group by trunc(g.start_date, 'Month'))
order by mth
Without few examples it's hard to say for sure that it's OK, it would be good to verify with something.
So if something is wrong please edit your post, add some example rows and explain.
Edit:
I think that creating the view may be more comfortable for you:
create or replace view v_monthly_sums as
Select trunc(g.start_date, 'Month') mth,
count(case when v.ACTUAL_SHIPDATE > v.PROMISE_DATE And v.ITEMNO Is Not Null
And v.CUMM_SHIPPED > 0 then V.ACTUAL_SHIPDATE end) d1,
Count(
case when v.ITEMNO Is Not Null And v.CUMM_SHIPPED > 0 then V.ACTUAL_SHIPDATE end
) d2
from glperiods g
left join v_releases_comb v
on trunc(v.ACTUAL_SHIPDATE, 'Month') = trunc(g.start_date, 'Month')
group by trunc(g.start_date, 'Month')
From this view you can select all values (select * from v_monthly_sums), to see what it contains, do any experiments,
but for your main question you can use something like:
select to_char(mth, 'Month') "Month",
case
when d2 = 0 then null
else round(1-d1/d2, 3)
end otd
from v_monthly_sums
where extract(Year From mth) = 2015 -- you can add here: AND D2<>0 to eliminate null rows
order by mth
GLPERIODS - I suspect that this table is not needed at all, not needed in our query/view for sure,
maybe you created it only to store dates for months which interests you.
But maybe I'm wrong and it is used somewhere else. It can by replaced in our view with simple hierarchical subquery,
but for now let's stay with current solution. Please insert one date for each month, I did not make any protections against more dates.
Why did not your query work? - you joined one number, let's say 0,567, produced by two other complicated queries with every day from table GLPERIODS. This is the reason why you got 12 months with the same, invaluable data.

Related

First_Day/Last_Day Query

Could you please help me to correct the script as below:
set LastDay=SELECT LAST_DAY(SYSDATE) FROM dual;
set FirstDay=Select trunc((sysdate),'month') as First_day_of_month from dual;
SELECT count(*) FROM tab1 g , h.ab1 LEFT JOIN tab2 h ON g.bba = h.bba
WHERE 1 = 1
AND g.DATE_ BETWEEN TO_DATE('FirstDay', 'YYYYMMDD') AND TO_DATE('LastDay', 'YYYYMMDD');
In Oracle, a DATE ALWAYS has year, month, day, hour, minute and second components. Using LAST_DAY(SYSDATE) only sets the year-month-day component of a date and does not modify the time component so if you filter from the start of the month to LAST_DAY(SYSDATE) then you will exclude any values from the last day of the month with a time component between the current time and 23:59:59.
What you want is to use:
SELECT count(*)
FROM tab1 g
CROSS JOIN h.ab1 -- your query is confusing around the joins
-- and may need fixing
LEFT JOIN tab2 h
ON g.bba = h.bba
WHERE g.DATE_ >= TRUNC(SYSDATE, 'MM')
AND g.DATE_ < ADD_MONTHS(TRUNC(SYSDATE, 'MM'), 1);
That's just
select count(*)
from tab1 g left join tab2 h on g.bba = h.bba
and g.date_ between trunc(sysdate, 'month') and last_day(sysdate);
Isn't it?

How to get last workday before holiday in Oracle [duplicate]

This question already has answers here:
How to get the previous working day from Oracle?
(4 answers)
Closed 1 year ago.
need help for some oracle stuff ..
I need to get Day-1 from sysdate, holiday and weekend will be excluded .
And for holiday, we need to get the range to get the last workday before holiday.
The start date and end date will coming from my holiday table.
ex :
Holiday Table
HolidayName
Start_date
End_Date
holiday1
5th Aug'21
6th Aug'21
condition :
this query run on 9th Aug 2021
expected result :
4th Aug'21
I've tried some query and function but I just can't get what I need.
Thanks a lot for help!
Here's one way to do it.
select max(d) as last_workday
from (select trunc(sysdate)-level as d from dual connect by level < 30) prior_month
where to_char(d, 'DY') not in ('SAT','SUN')
and not exists (select holidayname from holiday_table
where prior_month.d between start_date and end_date)
;
Without seeing your Holiday table, it's hard to say how many days back you would need to look to find the last workday. If you have a holiday that lasts for more than 30 days, you'll need to change the 30 to a larger number.
You can use a simple case expression to determine what day of the week the start of your holiday is, then subtract a number of days based on that.
WITH
holiday (holidayname, start_date, end_date)
AS
(SELECT 'holiday1', DATE '2021-8-5', DATE '2021-8-6' FROM DUAL
UNION ALL
SELECT 'Christmas', DATE '2021-12-25', DATE '2021-12-26' FROM DUAL
UNION ALL
SELECT 'July 4th', DATE '2021-7-4', DATE '2021-7-5' FROM DUAL)
SELECT holidayname,
start_date,
end_date,
start_date - CASE TO_CHAR (start_date, 'Dy') WHEN 'Mon' THEN 3 WHEN 'Sun' THEN 2 ELSE 1 END AS prior_business_day
FROM holiday;
HOLIDAYNAME START_DATE END_DATE PRIOR_BUSINESS_DAY
______________ _____________ ____________ _____________________
holiday1 05-AUG-21 06-AUG-21 04-AUG-21
Christmas 25-DEC-21 26-DEC-21 24-DEC-21
July 4th 04-JUL-21 05-JUL-21 02-JUL-21
You can use a recursive sub-query factoring clause from this answer:
WITH start_date (dt) AS (
SELECT DATE '2021-05-02' FROM DUAL
),
days ( dt, day, found ) AS (
SELECT dt,
TRUNC(dt) - TRUNC(dt, 'IW'),
0
FROM start_date
UNION ALL
SELECT dt - CASE day WHEN 0 THEN 3 WHEN 6 THEN 2 ELSE 1 END,
CASE WHEN day IN (0, 6, 5) THEN 4 ELSE day - 1 END,
CASE WHEN h.start_date IS NULL THEN 1 ELSE 0 END
FROM days d
LEFT OUTER JOIN holidays h
ON ( dt - CASE day WHEN 0 THEN 3 WHEN 6 THEN 2 ELSE 1 END
BETWEEN h.start_date AND h.end_date )
WHERE found = 0
)
SELECT dt
FROM days
WHERE found = 1;
Which, for the sample data:
CREATE TABLE holidays (HolidayName, Start_date, End_Date) AS
SELECT 'holiday1', DATE '2021-08-05', DATE '2021-08-06' FROM DUAL;
Outputs:
DT
2021-08-04 00:00:00
db<>fiddle here
Don't know if it's very efficient. Did it just for fun
create table holidays (
holiday_name varchar2(100) primary key,
start_date date not null,
end_date date not null
)
/
Table created
insert into holidays (holiday_name, start_date, end_date)
values ('holiday1', date '2021-08-05', date '2021-08-06');
1 row inserted
with days_before(day, wrk_day) as
(select trunc(sysdate - 1) d,
case
when h.holiday_name is not null then 0
when to_char(trunc(sysdate - 1), 'D') in ('6', '7') then 0
else 1
end work_day
from dual
left join holidays h
on trunc(sysdate - 1) between h.start_date and h.end_date
union all
select db.day - 1,
case
when h.holiday_name is not null then 0
when to_char(db.day - 1, 'D') in ('6', '7') then 0
else 1
end work_day
from days_before db
left join holidays h
on db.day - 1 between h.start_date and h.end_date
where db.wrk_day = 0) search depth first by day set order_no
select day from days_before where wrk_day = 1;
DAY
-----------
04.08.2021

How to compare system month and sales months?

I want to compare sales months with the system month. if equal get 1 output as new column(last_month) else 0. I tried below oracle query but I am getting a Null value.
SELECT a.agent_id,a.agent_name,a.ivr_registered_district,s.agent_type,s.district,s.province,a.parent_level1_id,a.parent_level1,a.sales_channel,TO_CHAR(TRUNC(a.connection_date, 'MONTH'), 'MON-YYYY') AS MONTHYEAR,
CASE
WHEN TO_CHAR(TRUNC(a.connection_date, 'MONTH'), 'MON-YYYY') = TO_CHAR(ADD_MONTHS(SYSDATE, - 1))
THEN 1
END as last_month
FROM EDW_TGT.FACT_LTE_SALES_CHANNELS a
JOIN SFA.sfa_agent_dtl s
ON a.agent_id = s.agent_id
where a.pre_post = 'LTE-PREPAID' and a.sales_channel in ('BUSINESS PARTNER', 'INSTITUSIONAL', 'REGIONAL TRADE PARTNERS', 'DISTRIBUTOR')
and a.connection_date >= TO_DATE('2020-01-01 00:00:0', 'YYYY-MM-DD HH24:MI:SS')
You don't need to truncate date as oracle provides you functionality to extract (day,month,year) from to_date function.
Your query should be like below.
SELECT a.agent_id,a.agent_name,a.ivr_registered_district,s.agent_type,s.district,s.province,a.parent_level1_id,a.parent_level1,a.sales_channel,TO_CHAR(TRUNC(a.connection_date, 'MONTH'), 'MON-YYYY') AS MONTHYEAR,
CASE
WHEN to_char(to_date(a.connection_date, 'DD-MM-YYYY'), 'Month') = to_char(to_date(sysdate-1, 'DD-MM-YYYY'), 'Month')
THEN 1
Else 0
END as last_month
FROM EDW_TGT.FACT_LTE_SALES_CHANNELS a
JOIN SFA.sfa_agent_dtl s
ON a.agent_id = s.agent_id
where a.pre_post = 'LTE-PREPAID' and a.sales_channel in ('BUSINESS PARTNER', 'INSTITUSIONAL', 'REGIONAL TRADE PARTNERS', 'DISTRIBUTOR')
and a.connection_date >= TO_DATE('2020-01-01 00:00:0', 'YYYY-MM-DD HH24:MI:SS')

Using Global Temporary Table in Subquery gives NULL

I am using following query.
SELECT SYSDATE,(SELECT P_PRICE_OPEN FROM GTT_ADJ_PRICE_TABLE WHERE FSYM_ID='P8R3C2-R' AND P_DATE='22-OCT-18' AND P_VOLUME<>0 AND ROWNUM=1) FROM DUAL;
GTT_ADJ_PRICE_TABLE is global temporary table loaded with values when i have executed a function related to it. This GTT preserves rows after commit. This query gives me correct results.
But, If i run the query
SELECT WEEK_END, WEEK_START,
(SELECT P_PRICE_OPEN FROM GTT_ADJ_PRICE_TABLE WHERE P_DATE=WEEK_START) AS WEEKS_OPEN_PRICE,
(SELECT MAX(P_PRICE_HIGH) FROM GTT_ADJ_PRICE_TABLE WHERE FSYM_ID=FID AND P_DATE<=WEEK_END
AND P_DATE>=WEEK_START AND P_VOLUME<>0) AS WEEKLY_HIGH,
(SELECT MIN(P_PRICE_LOW) FROM GTT_ADJ_PRICE_TABLE WHERE FSYM_ID=FID AND P_DATE<=WEEK_END
AND P_DATE>=WEEK_START AND P_VOLUME<>0) AS WEEKLY_LOW,
(SELECT SUM(P_VOLUME) FROM GTT_ADJ_PRICE_TABLE WHERE FSYM_ID=FID AND P_DATE<=WEEK_END
AND P_DATE>=WEEK_START AND P_VOLUME<>0) AS WEEKLY_VOLUME,
P_PRICE
FROM (
SELECT ROWNUM,FID,WEEK_END,P_VOLUME,P_PRICE,
(SELECT P_DATE FROM FP_V2_FP_BASIC_PRICES WHERE FSYM_ID=FID AND P_DATE>=TRUNC(WEEK_END, 'IW') AND P_VOLUME<>0 AND ROWNUM=1) AS WEEK_START
FROM (
SELECT
ROWNUM,FSYM_ID AS FID, WEEK_END,P_VOLUME, P_PRICE,P_PRICE_OPEN,P_PRICE_HIGH,P_PRICE_LOW
FROM (
SELECT ROWNUM,FSYM_ID,P_DATE AS WEEK_END, P_PRICE,P_VOLUME, P_PRICE_OPEN,P_PRICE_HIGH,P_PRICE_LOW,
CASE
WHEN (TO_CHAR(P_DATE,'D') >= AVG(TO_CHAR(P_DATE,'D')) OVER (order by P_DATE DESC rows between 1 preceding and current row) and ROWNUM>=1) or TO_CHAR(P_DATE,'D')=6
THEN 1
ELSE 0
END AS WEEKFLAG
FROM(
SELECT * FROM TABLE (ADJUSTED_PRICE('P8R3C2-R')) WHERE P_VOLUME<>0
)
)WHERE WEEKFLAG=1
)
);
It gives me NULL in WEEKS_OPEN_PRICE,WEEKLY_HIGH,WEEKLY_LOW,WEEKLY_VOLUME.
Please help me to resolve the issue. Thanks in anticipation.
Try with WITH clause. I hope a below query works. I don't have possibility to check.
WITH adjustedPrice AS (
SELECT *
FROM TABLE(adjusted_price('P8R3C2-R'))
WHERE p_volume <> 0
AND TO_CHAR(p_date, 'DY') NOT IN ('SAT', 'SUN')
)
SELECT ap.week,
ap.week_start,
ap.week_end,
ap.weeks_high_price,
ap.weeks_low_price,
ap.weekly_volume,
ws.p_price_open AS weeks_open_price,
we.p_price AS weeks_close_price
FROM (
SELECT TRUNC(p_date, 'IW') AS week,
TRUNC(MIN(p_date)) AS week_start,
TRUNC(MAX(p_date)) AS week_end,
MAX(p_price_high) AS weeks_high_price,
MIN(p_price_low) AS weeks_low_price,
SUM(p_volume) AS weekly_volume
FROM adjustedPrice
GROUP BY TRUNC(p_date, 'IW')
) ap
INNER JOIN adjustedPrice ws ON ws.p_date = ap.week_start
INNER JOIN adjustedPrice we ON we.p_date = ap.week_end
ORDER BY week DESC;
Wanted to post as a comment. But it wont fit there thats why writing it as an answer.
I tried this code to get the week start, end , weeks high price, weeks low price, weekly volume.
select
trunc("P_DATE", 'IW') as week,
min(trunc("P_DATE")) as week_start,
max(trunc("P_DATE")) as week_end,
MAX(P_PRICE_HIGH) AS WEEKS_HIGH_PRICE,
MIN(P_PRICE_LOW) AS WEEKS_LOW_PRICE,
SUM(P_VOLUME) AS WEEKLY_VOLUME
from TABLE
(ADJUSTED_PRICE('P8R3C2-R'))
WHERE
P_VOLUME<>0 AND to_char("P_DATE", 'DY') not in ('SAT','SUN')
group by
trunc("P_DATE", 'IW')
ORDER BY
trunc("P_DATE", 'IW') DESC;
That gave me results in hardly 2-4 seconds. But i want to get the weeks open price where date will be equal to WEEK_START and weeks closing price where date will be equal to WEEK_END.
I tried following approach for the same. But its taking too much time (like 300+ seconds).
SELECT
WEEK,WEEK_START, WEEK_END, WEEKS_HIGH_PRICE,WEEKS_LOW_PRICE,WEEKLY_VOLUME,
(SELECT P_PRICE_OPEN FROM TABLE (ADJUSTED_PRICE('P8R3C2-R')) WHERE P_VOLUME<>0 AND P_DATE=WEEK_START) AS WEEKS_OPEN_PRICE,
(SELECT P_PRICE FROM TABLE (ADJUSTED_PRICE('P8R3C2-R')) WHERE P_VOLUME<>0 AND P_DATE=WEEK_END) AS WEEKS_CLOSE_PRICE
FROM
(
select
trunc("P_DATE", 'IW') as week,
min(trunc("P_DATE")) as week_start,
max(trunc("P_DATE")) as week_end,
MAX(P_PRICE_HIGH) AS WEEKS_HIGH_PRICE,
MIN(P_PRICE_LOW) AS WEEKS_LOW_PRICE,
SUM(P_VOLUME) AS WEEKLY_VOLUME
from TABLE
(ADJUSTED_PRICE('P8R3C2-R'))
WHERE
P_VOLUME<>0 AND to_char("P_DATE", 'DY') not in ('SAT','SUN')
group by
trunc("P_DATE", 'IW')
ORDER BY
trunc("P_DATE", 'IW') DESC
);
If anybody can help me to improve the output time is most welcome.

Alternate for decode function

I have a table 'Holiday' which lists a set of holiday details.If i specify a date,I should obtain a result date after 5 days of specified date.If there is holiday in between it should exclude them and display the non holiday date.I have table named holiday which includes holiday date,holiday type|(weekly off,local holiday).Now i have used nested decode for continuous holiday checking.Tell me how this can be changed in case function.
DECODE
(date,
holidaydate, DECODE
(date + 1,
holidaydate + 1, DECODE
(date + 2,
holidaydate + 2, DECODE
(date + 3,holidaydate+3,date+4,date+3),date+2),date+1),date);
This can be achieved with a simple subquery which counts the number of holiday dates between a specified date and date+5. The following will return a date that is five non-holiday days in the future:
testdate+(select 5+count(1)
from holiday
where holidaydate between testdate
and testdate + 5)
Simply change both "5"s so another number to change the evaluation period.
SQLFiddle here
Edit - based on comment below, my code doesn't evaluate any days after the fifth day. This would probably be much easier with a function, but the following cte-based code will work also:
with cte as ( (select alldate,holidaydate
from (select to_date('20130101','yyyymmdd')+level alldate
from dual
connect by level < 10000 -- adjust for period to evaluate
) alldates
left join holiday on alldate=holidaydate) )
select
testdate,test_plus_five
from (
select
alldate test_plus_five,testdate,
sum(case when holidaydate is null
then 1
else 0 end) over (partition by testdate order by alldate) lastday
from
cte,
testdates
where
alldate >= testdate
group by
alldate,holidaydate,testdate)
where
lastday = 6
This script builds a calendar table so it can evaluate each day (holiday or non-holiday); then we get a running count of non-holiday days, and use the sixth one.
SQLFiddle here
AFAIK, You can use CASE alternative to DECODE in Oracle
CASE [ expression ]
WHEN condition_1 THEN result_1
WHEN condition_2 THEN result_2
...
WHEN condition_n THEN result_n
ELSE result
END
Finally i found the optimal solution.Thanks for ur response guys. SELECT dt FROM
(SELECT dt FROM (SELECT TO_DATE('15-AUG-2013','dd-mon-yyyy')+LEVEL dt FROM DUAL
CONNECT BY LEVEL < 30)
WHERE
(SELECT COUNT (*) FROM mst_holiday WHERE holidaydate = dt) = 0 )
where rownum=1

Resources