Change the year of a date to the current year in PL/SQL - oracle

I'm currently trying to do a comparison in my select. If the current date is before August 1st of the current year then display august 1st of the last year, otherwise display august 1st of this year. Essentially I'm trying to do:
CASE
WHEN (SYSDATE < 08/01/2015) THEN
08/01/2014
ELSE
08/01/2015
But I am at a loss as to how to get august for the month. So far I have:
TRUNC(SYSDATE, 'MON')
To get /01/ but how would I get it to constantly return august as the month? Would it be better to hardcode in the date and month and dynamically get the year instead? like 01/08/

Try something like this:
1 select sysdate,
2 trunc(sysdate,'YEAR'),
3 add_months(trunc(sysdate,'YEAR'),7),
4 add_months(trunc(sysdate,'YEAR'),7-12)
5* from dual
SQL> /
SYSDATE TRUNC(SYSDA ADD_MONTHS( ADD_MONTHS(
----------- ----------- ----------- -----------
31-jul-2015 01-jan-2015 01-aug-2015 01-aug-2014
SQL>
the columns are:
1) pulling the current sysdate.
2) converting to the first day of the year.
3) adding 7 months to get Aug 1 of current year.
4) -12 months to get Aug 1 of last year.
(that shows you the usage, you can figure out how to plug those suckers into your CASE statement ;) )

Related

Using current system month inside date functions

I am trying to write a query in Noetix that is pulling data from Oracle EBS. The query will have a column that checks to see if the date range of a field value of each record is within the current month then, if so, return another value. For example, the field value might be "23 JUN 2022", and I want to check to see if this date is within the "current" month.
So that I don't have to manually edit the report every time a month turns, I want the function to be 'rolling' where it checks the system time for the current month instead of me hard coding it in. I have the following expression, which works, but is static:
case
when
"TABLE1"."Scheduled_Date" >= TO_DATE('01 Jun 2022','DD Mon YYYY') AND
"TABLE1"."Scheduled_Date" < TO_DATE('01 Jul 2022','DD Mon YYYY') THEN
"TABLE1"."Selling_Price"
ELSE
TO_NUMBER('0')
END
How do I replace "Jun" and "Jul" in the expression above with a SYSDATE function that returns the current system month (for Jun), and the current system month +1 (for Jul)? I am experienced at MS Access SQL, but Oracle SQL is new to me. I can't figure out the proper syntax.
for the 1st day of the actual month you can use
add_months(last_day(trunc(sysdate))+1, -1)
for the 1st day of the next month you can use
last_day(trunc(sysdate))+1
There are various options you might choose; here's one of them (I'm setting date format so that you'd recognize values being returned; you don't have to do that):
SQL> alter session set nls_date_format = 'dd.mm.yyyy hh24:mi:ss';
Session altered.
SQL> select trunc(sysdate, 'mm') first_of_this_month,
2 add_months(trunc(sysdate, 'mm'), 1) first_of_next_nonth
3 from dual;
FIRST_OF_THIS_MONTH FIRST_OF_NEXT_NONTH
------------------- -------------------
01.06.2022 00:00:00 01.07.2022 00:00:00
SQL>
Applied to your code:
case when "TABLE1"."Scheduled_Date" >= trunc(sysdate, 'mm')
AND "TABLE1"."Scheduled_Date" < add_months(trunc(sysdate, 'mm'), 1)
THEN
"TABLE1"."Selling_Price"
ELSE
TO_NUMBER('0')
END

Oracle NUMTODSINTERVAL function and interval function

Can someone please explain what the below is doing?
and effective_DATE < (TRUNC(CURRENT_DATE) - NUMTODSINTERVAL(EXTRACT(DAY FROM TRUNC( CURRENT_DATE)), 'DAY' ) + INTERVAL '1' DAY)
I don't have access to the database so can not run it myself to test.
What I understand is that the NUMTODSINTERVAL converts a number to an INTERVAL DAY TO SECOND literal.
So say if I ran this query today it would check to see if the effective date is less than the 5th of Feb 2019 - 5 days converted to seconds plus 1 day?
So, 5th Feb - 000000005 + 1 = 6th Feb?
Is this correct or am I looking at this wrong?
Also why would developers use this method?
The second argument to NUMTODSINTERVAL specifies the unit. "INTERVAL DAY TO SECOND" is a confusing name - it just means an abstract interval of time, in this case 5 days.
EXTRACT(DAY FROM TRUNC( CURRENT_DATE)) finds the current day of the month (5 today)
NUMTODSINTERVAL(5, 'DAY') creates an INTERVAL of +5 days
So today it's saying, take Feb 5, subtract 5 days (which = Jan 31), then add one day (so Feb 1).
It seems to be trying to find the first day of the month, which would be much simpler as:
and effective_DATE < TRUNC(CURRENT_DATE, 'MM')

Oracle DB to_date with year - to_date(2017,'YYYY') unexpected return

While writing few queries I needed to return only those rows that have date column set in this year (2017) , that's not my problem I know how to write this query in couple of diffrent ways, but I came across something strange and unexpected for me. Can anyone explain why Oracle db 11.2 is behaving this way?
select sysdate from dual
returns:
2017/12/05 09:22:27
select to_date(2017,'YYYY'),trunc(sysdate,'YYYY') from dual
returns :
2017/12/01 00:00:00 2017/01/01 00:00:00
select to_date(2017,'YYYY'),trunc(sysdate,'YYYY') from dual
where trunc(sysdate,'YYYY') = to_date(2017,'YYYY')
no rows returned
Why does to_date(2017,'YYYY') returns 2017/12/01, will it return 2017/01/01 next month? Why does it work that way? I would expect it to always return 2017/01/01 no matter the current month (if month part is indeed changing depending on sysdate).
In Oracle, TO_DATE will assume that:
If you do not specify the year then it is the current year;
If you do not specify the month then it is the current month;
If you do not specify the day then it is the first day of the month;
If you do not specify the hours then it is the midnight hour (0);
If you do not specify the minutes then it is 0 minutes past the hour; and
If you do not specify the seconds then it is 0 seconds into the minute.
You are specifying only the year (2017) so it will be:
Zero minutes and seconds past midnight of the first day of the current month of the year you specify (2017).
If you want the first day of the year then specify the month (and preferably the rest of the date):
select to_date( '201701','YYYYMM'),
trunc(sysdate,'YYYY')
from dual
where trunc(sysdate,'YYYY') = to_date( '201701','YYYYMM' )
Or use a date literal:
select DATE '2017-01-01',
trunc(sysdate,'YYYY')
from dual
where trunc(sysdate,'YYYY') = DATE '2017-01-01'

MONTHS_BETWEEN Function

Can someone help me understand the working of Oracle Months_Between Function?
If I query select MONTHS_BETWEEN('02-28-2015', '01-28-2015')
I get an integer value of 1 but if I query
select MONTHS_BETWEEN('02-28-2015', '01-29-2015') I get 0.96.
Refer to the documentation. https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions089.htm
Note - the "31 day month" convention may cause weird results around month-ends. Consider:
select months_between(date '2016-07-02', date '2016-07-01') as one_day,
months_between(date '2016-07-01', date '2016-06-30') as another_day
from dual;
ONE_DAY ANOTHER_DAY
---------- -----------
.032258065 .064516129
1 row selected.
As if June had 31 days. It doesn't, but months_between treats it as though it did.
If you're working with just trying to determine the number of months in a set of months and don't care about the days. I find myself in this situation often... You can do a bit of date manipulation which is rather reliable for determining the number of months in a set of months. Say for instance Jul - Sep while starting with dates.
Thusly:
WITH MONTHS AS (
SELECT
SYSDATE DATE_ONE
, SYSDATE+57 DATE_TWO
FROM DUAL
)
SELECT
m.*
,TO_CHAR(m.DATE_ONE,'MON') START_MONTH
,TO_CHAR(m.DATE_TWO,'MON') END_MONTH
,MONTHS_BETWEEN(m.DATE_TWO,m.DATE_ONE) UNEXPECTED_RESULT
,MONTHS_BETWEEN(LAST_DAY(m.DATE_TWO),LAST_DAY(ADD_MONTHS(m.DATE_ONE,-1))) EXPECTED_RESULT
FROM MONTHS m
;

SYSDATE between date in two fields for 6 month period Q

I am trying to set between a date range for 6 months in the past for two different fields that will group the data by month. How do I set such a between clause to achieve this?
SELECT TO_CHAR(mopend, 'MM-yyyy') AS month, MOPSTATUS, COUNT(*) MTS_COMPLETE_CNT
FROM MOPUSER.MOPACTIVITY
WHERE UPPER(MOPSTATUS) = 'COMPLETE'
AND TO_CHAR(MOPACTIVITY.MOPSTART, 'yyyy-mm-dd hh24:mi') BETWEEN TO_CHAR(sysdate,'YYYY-MM-DD')||' 06:02:00' AND TO_CHAR(sysdate,'YYYY-MM-DD')||' 22:59:59'
OR TO_CHAR(MOPACTIVITY.MOPEND, 'yyyy-mm-dd hh24:mi') BETWEEN TO_CHAR(SYSDATE,'YYYY-MM-DD')||' 06:02:00' AND TO_CHAR(SYSDATE,'YYYY-MM-DD')||' 22:59:59'
GROUP BY TO_CHAR(mopend, 'MM-yyyy'), MOPSTATUS
ORDER BY TO_CHAR(mopend, 'MM-yyyy'), MOPSTATUS
I will answer one part of your question first, and then based on your comments, I can give you the full query.
The following query returns the end points between which you want to search. T1 is 06:02 in the morning on the date that is six months back in time. T2 is the last second of today.
select sysdate
,add_months( trunc(sysdate) + interval '06:02' hour to minute, -6) as t1
, trunc(sysdate) + interval '23:59:59' hour to second as t2
from dual;
The above query returns the following (using yyyy-mm-dd hh24:mi:ss):
sydate: 2014-04-11 13:54:28
t1: 2013-10-11 06:02:00
t2: 2014-04-11 23:59:59
If I interpret you correctly, this is the time period you want to search?
For the second part of the answer, I'd need to know the following:
Can any of MOPSTART or MOPEND be null? If so, how do you want to treat those rows?
Do you want to include the end points, i.e. rows where MOPSTART >= t1? Or only where MOTSTART > t1?
Same as (2) but for MOPEND
What month do you want to group by (see below)?
For example, row (a), do you want count it once for each month, or only in JAN (started) or only in JUN(ended)?
JAN FEB MAR APR MAY JUN
a: |-------------------|
b: |---|---|
c: |---|
d: |-----------|
e: |--------|

Resources