weekly calender table in oracle to update alternative week flag & date - oracle

I'm creating a table to populate a weekly calendar table and my requirement is to update wk_start_date as every week sunday and populate the pr_wk_flag as Y every two weeks and
beg dt & end dt would be from two weeks before Thursday to current Wednesday
Like below, How to populate in oracle plsql
Wk_start_date pr_wk_flag beg dt end dt
2/9/2014 Y 1/30/2014 2/12/2014
2/16/2014 N NULL NULL
2/23/2014 Y 2/13/2014 2/26/2014
3/2/2014 N NULL NULL
3/9/2014 Y 2/27/2014 3/12/2014

Here's one way:
select wk_start_dt, pr_wk_flag,
case when pr_wk_flag = 'Y' then wk_start_dt - 10 end as beg_dt,
case when pr_wk_flag = 'Y' then wk_start_dt + 3 end as end_dt
from (
select next_day(date '2014-02-01' + (7 * level), 'SUN') as wk_start_dt,
decode(mod(level, 2), 1, 'Y', 'N') as pr_wk_flag
from dual
connect by level <= 6
);
The inner query is a common way to generate a sequence of values. Adjust the start date and the number of iterations to change the range of week-starts generated, and if necessary to flip the flag value. The SUN argument to the next_day function has to be in your local session language, so change that to something suitable if you're not using English. As a one-off insert I don't think the NLS-sensitivity is an issue in this case.
The outer query just uses the flag to decide whether to calculate the other two dates, or leave them null.
SQL Fiddle demo.

There you have for all 2014 year
SELECT dates+(8- to_char(TRUNC (TRUNC (dates, 'w'), 'YEAR'),'d')) wk_start_date,
DECODE (MOD (TO_NUMBER (TO_CHAR (dates, 'w')), 2), 0, 'Y', 'N')
pr_wk_flag,
DECODE (MOD (TO_NUMBER (TO_CHAR (dates, 'w')), 2),
0, dates + 5 - 17,
NULL)
pr_wk_flag,
DECODE (MOD (TO_NUMBER (TO_CHAR (dates, 'w')), 2), 0, dates + 3, NULL)
end_dt
FROM (SELECT TRUNC (SYSDATE, 'YEAR') + 7 * (ROWNUM - 1) dates
FROM dba_tables
WHERE TRUNC (TRUNC (SYSDATE, 'YEAR') + 7 * (ROWNUM - 1), 'YEAR') =
TRUNC (SYSDATE, 'YEAR'))
ORDER BY wk_start_date ASC

Related

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

get data in 'n' hours interval between two dates oracle

i am trying to get data between two dates in an 'n' hour intervals ... the problem is that i am not getting desired result after 8 hour intervals ... my value in 'n' interval can range up to any number between 1 to 120.
Following is the Pseudo code of what i am tying to do:
-- i first select number of hours between two dates
SELECT
24 * (SYSDATE - to_date('2018-04-16 15:20', 'YYYY-MM-DD hh24:mi')) AS diff_hours
FROM dual;
-- Then i use the above value in CONNECT BY ROWNUM <= ROUND((hours between two dates/n),0) to get data in n intervals
SELECT TRUNC(sysdate - (rownum/ROUND((24/n),0)),'HH24') as the_hour
FROM dual
CONNECT BY ROWNUM <= ROUND((hours between two dates/n),0) ;
Sample query
SELECT
24 * (SYSDATE - to_date('2018-04-16 15:20', 'YYYY-MM-DD hh24:mi')) AS diff_hours
FROM dual;
SELECT TRUNC(sysdate - (rownum/ROUND((24/8),0)),'HH24') as the_hour
FROM dual
CONNECT BY ROWNUM <= ROUND((724/8),0) ;
How can i change above query to get data in 'n' hour intervals between two dates, with n being any number of hours?
Remove the first ROUND() in your SELECT
SELECT TRUNC (SYSDATE - (ROWNUM / (24 / 9)), 'HH24') AS the_hour
FROM DUAL
CONNECT BY ROWNUM <= ROUND ( (724 / 9), 0);
try this,
SELECT TO_DATE(:p_date1, 'MM/DD/YYYY') + (FLOOR((rownum*:p_interval)/24) + (MOD((rownum*:p_interval), 24)/24)) dt
FROM DUAL
CONNECT BY (rownum*:p_interval) <= ((TO_DATE(:p_date2, 'MM/DD/YYYY')+.5) - TO_DATE(:p_date1, 'MM/DD/YYYY') + (MOD((rownum*:p_interval), 24)/24)) * 24
ORDER BY 1;

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

Get First Day Of Week From Week Number

In Oracle, is there a straightforward way to get the first day of the week given a week number?
For example, today's date is 12/4/2012. If I run:
select to_char(sysdate,'WW') from dual;
It returns 49 for the week number.
What I would like to do is somehow return 12/2/2012 for the first day...given week 49 (assuming Sunday as first day of the week).
Any ideas? Thanks in advance for any help!
try this:
select next_day(max(d), 'sun') requested_sun
from (select to_date('01-01-2012', 'dd-mm-yyyy') + (rownum-1) d from dual connect by level <= 366)
where to_char(d, 'ww') = 49-1;
just set your year to_date('01-01-2012' and week number-1 49-1 as applicable.
the sunday in the 49th week of 2008?
SQL> select next_day(max(d), 'sun') requested_sun
2 from (select to_date('01-01-2008', 'dd-mm-yyyy') + (rownum-1) d from dual connect by level <= 366)
3 where to_char(d, 'ww') = 49-1;
REQUESTED
---------
07-DEC-08
and 2012
SQL> select next_day(max(d), 'sun') requested_sun
2 from (select to_date('01-01-2012', 'dd-mm-yyyy') + (rownum-1) d from dual connect by level <= 366)
3 where to_char(d, 'ww') = 49-1;
REQUESTED
---------
02-DEC-12
Try this,
select
next_day(trunc(to_date(in_year,'yyyy'),'yyyy') -1,'Mon') + (7 * (in_week - 1))
from dual;
If you have the date, not just the week number, you can try this:
Get the day number of the week of your date with: to_char(theDate, 'D')
substract that number from your date plus 1, and you'll get the Sunday of that week.
Add 7 and you'll get the date of end of the week(Saturday).
Like this:
SELECT theDate - to_char(theDate, 'D') + 1 as BeginOfWeek,
theDate,
theDate - to_char(theDate, 'D') + 7 as EndOfWeek
FROM TableName
I can't comment on questions yet, so I'll add another one. But this is based on #Dazzals answer.
His solution doesn't work for week one and for ISO-weeks. Also it doesn't work, if the first day of the week is not sunday, which can be controlled by NLS_SETTINGS.
This one does:
SELECT MIN(D)
FROM (SELECT TO_DATE('01-01-2013', 'dd-mm-yyyy') + (ROWNUM-10) D, ROWNUM R
FROM DUAL
CONNECT BY LEVEL <= 376)
WHERE TO_CHAR(D,'IYYYIW') = '201301'
Because we are spanning more than one year, we need to check the year too.
Using the trunc function #Justin used, I think this is what you want:
select trunc(to_date('2012-01-01', 'YYYY-MM-DD') + (49 - 1) * 7, 'WW') from dual;
I ended up doing this:
function getFirstDayOfWeek(y in binary_integer, w in binary_integer) return date
is
td date;
begin
td:=TO_DATE(TO_CHAR(y)||'0101', 'YYYYMMDD');
for c in 0..52
loop
if TO_NUMBER(TO_CHAR(td, 'IW'))=w then
return TRUNC(td, 'IW');
end if;
td:=td+7;
end loop;
return null;
end;

Oracle date difference to get number of years

Is there a way to calculate the number of years between dates. Not sure how to do this while accounting for leap and what not. Is it possible to do an IF statement maybe in the SELECT?
Thanks
I'd use months_between, possibly combined with floor:
select floor(months_between(date '2012-10-10', date '2011-10-10') /12) from dual;
select floor(months_between(date '2012-10-9' , date '2011-10-10') /12) from dual;
floor makes sure you get down-rounded years. If you want the fractional parts, you obviously want to not use floor.
If you just want the difference in years, there's:
SELECT EXTRACT(YEAR FROM date1) - EXTRACT(YEAR FROM date2) FROM mytable
Or do you want fractional years as well?
SELECT (date1 - date2) / 365.242199 FROM mytable
365.242199 is 1 year in days, according to Google.
I had to implement a year diff function which works similarly to sybase datediff. In that case the real year difference is counted, not the rounded day difference. So if there are two dates separated by one day, the year difference can be 1 (see select datediff(year, '20141231', '20150101')).
If the year diff has to be counted this way then use:
EXTRACT(YEAR FROM date_to) - EXTRACT(YEAR FROM date_from)
Just for the log the (almost) complete datediff function:
CREATE OR REPLACE FUNCTION datediff (datepart IN VARCHAR2, date_from IN DATE, date_to IN DATE)
RETURN NUMBER
AS
diff NUMBER;
BEGIN
diff := CASE datepart
WHEN 'day' THEN TRUNC(date_to,'DD') - TRUNC(date_from, 'DD')
WHEN 'week' THEN (TRUNC(date_to,'DAY') - TRUNC(date_from, 'DAY')) / 7
WHEN 'month' THEN MONTHS_BETWEEN(TRUNC(date_to, 'MONTH'), TRUNC(date_from, 'MONTH'))
WHEN 'year' THEN EXTRACT(YEAR FROM date_to) - EXTRACT(YEAR FROM date_from)
END;
RETURN diff;
END;";
Need to find difference in year, if leap year the a year is of 366 days.
I dont work in oracle much, please make this better.
Here is how I did:
SELECT CASE
WHEN ( (fromisleapyear = 'Y') AND (frommonth < 3))
OR ( (toisleapyear = 'Y') AND (tomonth > 2)) THEN
datedif / 366
ELSE
datedif / 365
END
yeardifference
FROM (SELECT datedif,
frommonth,
tomonth,
CASE
WHEN ( (MOD (fromyear, 4) = 0)
AND (MOD (fromyear, 100) <> 0)
OR (MOD (fromyear, 400) = 0)) THEN
'Y'
END
fromisleapyear,
CASE
WHEN ( (MOD (toyear, 4) = 0) AND (MOD (toyear, 100) <> 0)
OR (MOD (toyear, 400) = 0)) THEN
'Y'
END
toisleapyear
FROM (SELECT (:todate - :fromdate) AS datedif,
TO_CHAR (:fromdate, 'YYYY') AS fromyear,
TO_CHAR (:fromdate, 'MM') AS frommonth,
TO_CHAR (:todate, 'YYYY') AS toyear,
TO_CHAR (:todate, 'MM') AS tomonth
FROM DUAL))
For Oracle SQL Developer I was able to calculate the difference in years using the below line of SQL. This was to get Years that were within 0 to 10 years difference. You can do a case like shown in some of the other responses to handle your ifs as well. Happy Coding!
TRUNC((MONTHS_BETWEEN(<DATE_ONE>, <DATE_TWO>) * 31) / 365) > 0 and TRUNC((MONTHS_BETWEEN(<DATE_ONE>, <DATE_TWO>) * 31) / 365) < 10

Resources