Between MOnths In oracle when date is greater than 10 of every month - oracle

My question is basically i want to increment months by one USING MONTHS_BETWEEN IN ORACLE
when date is greater than 10 of every month my query is :
CASE
when
TRUNC( months_between(TO_DATE(K.RECORD_DATE,'DD/MM/YYYY'),TO_DATE(K.DUE_DATE,'DD/MM/YYYY')) ) <= 0 then 0
when
--to_number(to_char(K.RECORD_DATE,'dd')) >10
TO_NUMBER(TO_CHAR( TO_DATE(k.RECORD_DATE,'DD/MM/YYYY'),'DD')) > 10
then
TRUNC( months_between(K.RECORD_DATE,K.DUE_DATE) )+1
else
TRUNC( months_between(K.RECORD_DATE,K.DUE_DATE) )
end as mths
FROM
TBL_PAYMENT_DTL K

Use to_date() in months_between().
select
CASE
when
TRUNC( months_between(TO_DATE(K.RECORD_DATE,'DD/MM/YYYY'),TO_DATE(K.DUE_DATE,'DD/MM/YYYY')) ) <= 0 then 0
when
--to_number(to_char(K.RECORD_DATE,'dd')) >10
TO_NUMBER(TO_CHAR( TO_DATE(k.RECORD_DATE,'DD/MM/YYYY'),'DD')) > 10
then
TRUNC( months_between(TO_DATE(k.RECORD_DATE,'DD/MM/YYYY'),TO_DATE(k.DUE_DATE,'DD/MM/YYYY')) )+1
else
TRUNC( months_between(TO_DATE(k.RECORD_DATE,'DD/MM/YYYY'),TO_DATE(k.DUE_DATE,'DD/MM/YYYY')) )
end as mths
FROM
TBL_PAYMENT_DTL K

You can use the EXTRACT function to get the day part of the date, as below:
SELECT
CASE
when
extract(day from K.RECORD_DATE) > 10 then K.RECORD_DATE
else
add_months(K.RECORD_DATE, 1)
end as mths
from
TBL_PAYMENT_DTL K

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 percentage of increase/decrease from two results base on their WHERE clause

I don't know if my Title is understandable but what I am really trying to achieve is get the percentage increase/decrease of Result 2 from Result 1.
I have a SALES table with these sample data:
DATE. AMOUNT
01-JAN-20. 500
02-JAN-20. 400
...
15-MAR-20. 1000
Assume that the table is filled with daily sales.
Now, with the Dashboard app I'm trying to make in APEX, I want to display the TOTAL AMOUNT base from the DATE the user will choose on a SELECT list (Today, This Week, This Month).
So basically, these would be the queries:
SELECT SUM(AMOUNT) TOTAL_SALES FROM TB_SALES WHERE DATE = TRUNC(SYSDATE); - Today
SELECT SUM(AMOUNT) TOTAL_SALES FROM TB_SALES WHERE DATE BETWEEN TRUNC(SYSDATE-7,'SUNDAY') AND TRUNC(SYSDATE); - This Week
SELECT SUM(AMOUNT) TOTAL_SALES FROM TB_SALES WHERE DATE BETWEEN TRUNC(SYSDATE, 'MM' AND TRUNC(SYSDATE); - This Month
To get the percentage of increase or decrease, I now then need to get the result of same queries but different DATE from the WHERE clause.
So with Yesterday, I should query
TRUNC(SYSDATE -1);
And so on. Actually I still don't know how to get the This week part.
With the two results, I can now calculate the percentage.
What I can't figure out is how can I query this two WHERE clause of two DATEs. My current temporary solution is to query the second query and just use JavaScript to update my Card.
I found something about LAG functions but cannot find any sample which uses WHERE clause to compare the results.
EDIT:
To be more clear, this is what I want to compare and get the percentage.
Today VS Yesterday
This week VS Last week
This month VS Last month
You can use conditional aggregation to find the totals:
SELECT SUM(
CASE
WHEN "DATE" >= TRUNC( SYSDATE )
AND "DATE" <= SYSDATE
THEN AMOUNT
ELSE NULL
END
) AS total_today,
SUM(
CASE
WHEN "DATE" >= TRUNC( SYSDATE ) - INTERVAL '1' DAY
AND "DATE" < TRUNC( SYSDATE )
THEN AMOUNT
ELSE NULL
END
) AS total_yesterday,
SUM(
CASE
WHEN "DATE" >= NEXT_DAY( TRUNC( SYSDATE ) - INTERVAL '7' DAY, 'SUNDAY' )
AND "DATE" <= SYSDATE
THEN AMOUNT
ELSE NULL
END
) AS total_this_week,
SUM(
CASE
WHEN "DATE" >= NEXT_DAY( TRUNC( SYSDATE ) - INTERVAL '14' DAY, 'SUNDAY' )
AND "DATE" < NEXT_DAY( TRUNC( SYSDATE ) - INTERVAL '7' DAY, 'SUNDAY' )
THEN AMOUNT
ELSE NULL
END
) AS total_last_week,
SUM(
CASE
WHEN "DATE" >= TRUNC( SYSDATE, 'IW' ),
AND "DATE" <= SYSDATE
THEN AMOUNT
ELSE NULL
END
) AS total_this_iso_week,
SUM(
CASE
WHEN "DATE" >= TRUNC( SYSDATE, 'IW' ) - INTERVAL '7' DAY,
AND "DATE" < TRUNC( SYSDATE, 'IW' )
THEN AMOUNT
ELSE NULL
END
) AS total_last_iso_week,
SUM(
CASE
WHEN "DATE" >= TRUNC( SYSDATE ) - INTERVAL '6' DAY,
AND "DATE" <= SYSDATE
THEN AMOUNT
ELSE NULL
END
) AS total_this_7_days,
SUM(
CASE
WHEN "DATE" >= TRUNC( SYSDATE ) - INTERVAL '13' DAY,
AND "DATE" < TRUNC( SYSDATE ) - INTERVAL '6' DAY
THEN AMOUNT
ELSE NULL
END
) AS total_last_7_days,
SUM(
CASE
WHEN "DATE" >= TRUNC( SYSDATE, 'MM' )
AND "DATE" <= SYSDATE
THEN AMOUNT
ELSE NULL
END
) AS total_this_month
SUM(
CASE
WHEN "DATE" >= ADD_MONTHS( TRUNC( SYSDATE, 'MM' ), -1 )
AND "DATE" < TRUNC( SYSDATE, 'MM' )
THEN AMOUNT
ELSE NULL
END
) AS total_this_month
FROM tb_sales
WHERE "DATE" >= ADD_MONTHS( TRUNC( SYSDATE, 'MM' ), -1 )
AND "DATE" <= SYSDATE;
This will find the totals for:
Today and yesterday;
This week and last week (starting from midnight Sunday until before the midnight of the next Sunday or until this instant, whichever is earlier);
This ISO 8601 week and last ISO 8601 week (starting midnight Monday);
The last 7 days (today and the previous 6 days) and 7 days before that; and
This month and last month (from midnight of the first day of the month until this instant).
If you want the percentage increases compared to a previous period then:
SELECT 100 * ( total_this_today / total_yesterday - 1 ) AS percent_increase_this_today,
100 * ( total_this_week / total_last_week - 1 ) AS percent_increase_this_week,
100 * ( total_this_iso_week / total_last_iso_week - 1 ) AS percent_increase_this_iso_week,
100 * ( total_this_7_days / total_last_7_days - 1 ) AS percent_increase_this_7_days,
100 * ( total_this_month / total_last_month - 1 ) AS percent_increase_this_month
FROM (
SELECT SUM(
CASE
WHEN "DATE" >= TRUNC( SYSDATE )
AND "DATE" <= SYSDATE
THEN AMOUNT
ELSE NULL
END
) AS total_today,
SUM(
CASE
WHEN "DATE" >= TRUNC( SYSDATE ) - INTERVAL '1' DAY
AND "DATE" < TRUNC( SYSDATE )
THEN AMOUNT
ELSE NULL
END
) AS total_yesterday,
SUM(
CASE
WHEN "DATE" >= NEXT_DAY( TRUNC( SYSDATE ) - INTERVAL '7' DAY, 'SUNDAY' )
AND "DATE" <= SYSDATE
THEN AMOUNT
ELSE NULL
END
) AS total_this_week,
SUM(
CASE
WHEN "DATE" >= NEXT_DAY( TRUNC( SYSDATE ) - INTERVAL '14' DAY, 'SUNDAY' )
AND "DATE" < NEXT_DAY( TRUNC( SYSDATE ) - INTERVAL '7' DAY, 'SUNDAY' )
THEN AMOUNT
ELSE NULL
END
) AS total_last_week,
SUM(
CASE
WHEN "DATE" >= TRUNC( SYSDATE, 'IW' ),
AND "DATE" <= SYSDATE
THEN AMOUNT
ELSE NULL
END
) AS total_this_iso_week,
SUM(
CASE
WHEN "DATE" >= TRUNC( SYSDATE, 'IW' ) - INTERVAL '7' DAY,
AND "DATE" < TRUNC( SYSDATE, 'IW' )
THEN AMOUNT
ELSE NULL
END
) AS total_last_iso_week,
SUM(
CASE
WHEN "DATE" >= TRUNC( SYSDATE ) - INTERVAL '6' DAY,
AND "DATE" <= SYSDATE
THEN AMOUNT
ELSE NULL
END
) AS total_this_7_days,
SUM(
CASE
WHEN "DATE" >= TRUNC( SYSDATE ) - INTERVAL '13' DAY,
AND "DATE" < TRUNC( SYSDATE ) - INTERVAL '6' DAY
THEN AMOUNT
ELSE NULL
END
) AS total_last_7_days,
SUM(
CASE
WHEN "DATE" >= TRUNC( SYSDATE, 'MM' )
AND "DATE" <= SYSDATE
THEN AMOUNT
ELSE NULL
END
) AS total_this_month
SUM(
CASE
WHEN "DATE" >= ADD_MONTHS( TRUNC( SYSDATE, 'MM' ), -1 )
AND "DATE" < TRUNC( SYSDATE, 'MM' )
THEN AMOUNT
ELSE NULL
END
) AS total_this_month
FROM tb_sales
WHERE "DATE" >= ADD_MONTHS( TRUNC( SYSDATE, 'MM' ), -1 )
AND "DATE" <= SYSDATE
)

Oracle: Days between two date and Exclude weekdays how to handle negative numbers

I have two date columns and trying to measure days between the two dates excluding weekends. I'm getting a negative number and need help solving.
Table
CalendarDate DayNumber FirstAssgn FirstCnt DayNumber2 Id BusinessDays
5/21/2017 Sunday 5/21/17 5/21/17 Sunday 1 -1
Query:
TRUNC(TO_DATE(A.FIRST_CONTACT_DT, 'DD/MM/YYYY')) - TRUNC(TO_DATE(A.FIRST_ASSGN_DT, 'DD/MM/YYYY'))
- ((((TRUNC(A.FIRST_CONTACT_DT,'D'))-(TRUNC(A.FIRST_ASSGN_DT,'D')))/7)*2)
- (CASE WHEN TO_CHAR(A.FIRST_ASSGN_DT,'DY','nls_date_language=english') ='SUN' THEN 1 ELSE 0 END)
- (CASE WHEN TO_CHAR(A.FIRST_CONTACT_DT,'DY','nls_date_language=english')='SAT' THEN 1 ELSE 0 END)
- (SELECT COUNT(1) FROM HUM.CALENDAR CAL
WHERE 1=1
AND CAL.CALENDAR_DATE >= A.FIRST_ASSGN_DT
AND CAL.CALENDAR_DATE < A.FIRST_CONTACT_DT
--BETWEEN A.FIRST_ASSGN_DT AND A.FIRST_CONTACT_DT
AND CAL.GRH_HOLIDAY_IND = 'Y'
) AS Business_Days
Looks like below piece needs editing...
- (CASE WHEN TO_CHAR(A.FIRST_ASSGN_DT,'DY','nls_date_language=english')='SUN' THEN 1 ELSE 0 END)
Adapted from my answer here:
Get the number of days between the Mondays of both weeks (using TRUNC( datevalue, 'IW' ) as an NLS_LANGUAGE independent method of finding the Monday of the week) then add the day of the week (Monday = 1, Tuesday = 2, etc., to a maximum of 5 to ignore weekends) for the end date and subtract the day of the week for the start date. Like this:
SELECT ( TRUNC( end_date, 'IW' ) - TRUNC( start_date, 'IW' ) ) * 5 / 7
+ LEAST( end_date - TRUNC( end_date, 'IW' ) + 1, 5 )
- LEAST( start_date - TRUNC( start_date, 'IW' ) + 1, 5 )
AS WeekDaysDifference
FROM your_table
With RANGE_TEMP as (
SELECT
STARTPERIOD start_date,
ENDPERIOD end_date
FROM
TABLE_DATA -- YOUR TABLE WITH ALL DATA DATE
), DATE_TEMP AS (
SELECT
(start_date + LEVEL) DATE_ALL
FROM
RANGE_TEMP
CONNECT BY LEVEL <= (end_date - start_date)
), WORK_TMP as (
SELECT
COUNT(DATE_ALL) WORK_DATE
FROM
DATE_TEMP
WHERE
TO_CHAR(DATE_ALL,'D', 'NLS_DATE_LANGUAGE=ENGLISH') NOT IN ('1','7')
), BUSINESS_TMP as (
SELECT
COUNT(DATE_ALL) BUSINESS_DATE
FROM
DATE_TEMP
WHERE
TO_CHAR(DATE_ALL,'D', 'NLS_DATE_LANGUAGE=ENGLISH') IN ('1','7')
)
SELECT
L.WORK_DATE,
H.BUSINESS_DATE
FROM
BUSINESS_TMP H,
WORK_TMP L
;

Find number of Wednesdays (or other weekday) in a month - Oracle SQL

I found this query for finding the number of Sundays in a month.
I have been tinkering with it but cannot figure out how to change it to get, say, the number of Wednesdays in a month, for example. Can you show me how?
with
months as (
select add_months(trunc(sysdate,'YEAR'),level-13) month
from dual
connect by level <= 36
)
select to_char(month,'YYYY') year,
to_char(month,'Month') month,
to_char(month,'Day') first_day,
to_char(last_day(month),'Day DD') last_day,
4+
case
when to_char(last_day(month),'DD') - decode(to_char(month,'D'),1,0,8 -to_char(month,'D')) >= 29
then 1
else 0
end nb_sunday
from months
Here's the game: You give me a year (like 2015) and a day of the week, in the form of a three-letter string (like 'Wed'). I will return a table with each month of that year and with the count of days-of-week equal to your input in each month.
Simply implementing here the suggestion from my Comment to MT0's Answer. I am hard-coding the year and the day-of-week (in a CTE) since "how to pass parameters to a query" (through bind variables and such) is not the focus in this thread.
with
inputs ( yr, day_of_week ) as (
select 2015, 'Wed' from dual
),
prep ( dec31 ) as (
select to_date(to_char(yr - 1) || '-12-31', 'yyyy-mm-dd') from inputs
)
select to_char(add_months(dec31, level), 'Mon-yyyy') as mth,
( next_day(add_months(dec31, level) , day_of_week) -
next_day(add_months(dec31, level - 1), day_of_week) ) / 7 as cnt
from inputs cross join prep
connect by level <= 12;
MTH CNT
-------- ----
Jan-2015 4
Feb-2015 4
Mar-2015 4
Apr-2015 5
May-2015 4
Jun-2015 4
Jul-2015 5
Aug-2015 4
Sep-2015 5
Oct-2015 4
Nov-2015 4
Dec-2015 5
12 rows selected.
The last wednesday of the month is given by:
TRUNC( NEXT_DAY( LAST_DAY( :month ) - INTERVAL '7' DAY, 'WEDNESDAY' ) )
The first wednesday of the month is given by:
NEXT_DAY( TRUNC( :month, 'MM' ) - INTERVAL '1' DAY, 'WEDNESDAY' )
Subtracting gives the number of days between them. Divide by 7 and add 1 and you get the number of Wednesdays:
SELECT ( TRUNC( NEXT_DAY( LAST_DAY( :month ) - INTERVAL '7' DAY, 'WEDNESDAY' ) )
- NEXT_DAY( TRUNC( :month, 'MM' ) - INTERVAL '1' DAY, 'WEDNESDAY' )
) / 7 + 1
AS number_of_wednesdays
FROM DUAL;
Or you can just use the difference between the first Wednesday of the month and of the next month as suggested by #mathguy
SELECT ( NEXT_DAY(
ADD_MONTHS( TRUNC( :month, 'MM' ), 1 ) - INTERVAL '1' DAY,
'WEDNESDAY'
)
- NEXT_DAY(
TRUNC( :month, 'MM' ) - INTERVAL '1' DAY,
'WEDNESDAY'
)
) / 7
AS number_of_wednesdays
FROM DUAL;

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