ORA-01839: date not valid for month specified - sql

The following query returns
select to_char( trunc(sysdate) - numtoyminterval(level - 1, 'month'), 'mon-yy') as month from dual connect by level <= 12
last 12 months according to today's date(i.e. 2-Jan-18).
Say if today's date is 29-DEC-17 it gives oracle sql error:
ORA-01839: date not valid for month specified
(since on subtracting there would be a date in the result as '29-FEB-17' which is not possible). So on specific dates this error would pop-up. How do you suggest to overcome this?

I prefer ADD_MONTHS (TRUNC of a date literal is stupid, but I left it as you'd have SYSDATE anyway, wouldn't you?):
SQL> select to_char(add_months(trunc(date '2017-12-29'), -level), 'dd-mon-yy',
2 'nls_Date_language = english') as month
3 from dual
4 connect by level <= 12;
MONTH
------------------
29-nov-17
29-oct-17
29-sep-17
29-aug-17
29-jul-17
29-jun-17
29-may-17
29-apr-17
29-mar-17
28-feb-17
29-jan-17
29-dec-16
12 rows selected.
SQL>

This behavior of INTERVAL YEAR TO MONTH is as documented, see Datetime/Interval Arithmetic
You should consider function ADD_MONTHS:
If date is the last day of the month or if the resulting month has
fewer days than the day component of date, then the result is the last
day of the resulting month. Otherwise, the result has the same day
component as date.
It depends on your requirements what you consider as "right". In fact "one month" does not have a fixed duration.

Related

PLSQL - How to find Monday and Friday of the week of a given date

I have spent days trying to figure this out to no avail, so hopefully someone can help me. I have a queried date set which contains several fields including a column of dates. What I want to do is create a new field in my query that tells what the Monday and Friday is for the week of that row's particular date.
So for example; if the date in one of my rows is "1/16/18",
the new field should indicate "1/15/18 - 1/19/18".
So basically I need to be able to extract the Monday date (1/15/18) and the Friday date (1/19/18) of the week of 1/16/18 and then concatenate the two with a dash ( - ) in between. I need to do this for every row.
How on earth do I do this? I've been struggling just to figure out how to find the Monday or Friday of the given date...
Assuming that your column is of type date, you can use trunc to get the first day of the week (monday) and then add 4 days to get the friday.
For example:
with yourTable(d) as (select sysdate from dual)
select trunc(d, 'iw'), trunc(d, 'iw') + 4
from yourTable
To format the date as a string in the needed format, you can use to_char; for example:
with yourTable(d) as (select sysdate from dual)
select to_char(trunc(d, 'iw'), 'dd/mm/yy') ||'-'|| to_char(trunc(d, 'iw') + 4, 'dd/mm/yy')
from yourTable
gives
15/01/2018-19/01/18
There may be a simpler, canonical Oracle method to this but you can still reduce it to a simple calculation on your own either way. I'm going to assume you're dealing with only dates falling Monday through Friday. If you do need to deal with weekend dates then you might have to be more explicit about which logical week they should be attached to.
<date> - (to_char(<date>, 'D') - 2) -- Monday
<date> + (6 - to_char(<date>, 'D')) -- Friday
In principle all you need to do is add/subtract the appropriate number of days based on the current day of week (from 1 - 7). There are some implicit casts going on in there and it would probably be wise to handle those better. You might also want to check into NLS settings to make sure you can rely on to_char() using Sunday as the first day of week.
https://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements004.htm
You can also use the NEXT_DAY function, as in:
SELECT TRUNC(NEXT_DAY(SYSDATE, 'MON')) - INTERVAL '7' DAY AS PREV_MONDAY,
TRUNC(NEXT_DAY(SYSDATE, 'FRI')) AS NEXT_FRIDAY
FROM DUAL;
Note that using the above, on weekends the Monday will be the Monday preceding the current date, and the Friday will be the Friday following the current date, i.e. there will be 11 days between the two days.
You can also use
SELECT TRUNC(NEXT_DAY(SYSDATE, 'MON')) - INTERVAL '7' DAY AS PREV_MONDAY,
TRUNC(NEXT_DAY(SYSDATE, 'MON')) - INTERVAL '3' DAY AS NEXT_FRIDAY
FROM DUAL;
in which case the Monday and Friday will always be from the same week, but if SYSDATE is on a weekend the Monday and Friday returned will be from the PREVIOUS week.

SQL +/- number meaning?

I could not find the meaning of the following SQL command:
where date between to_date('2013-03-01', 'yyyy-mm-dd') and trunc(sysdate, 'mm') -1
What does the "-1" mean / does?
The other example is
trunc(months_between(date1, date2))+1
I have searched for this, but could not find a thing.
Thank you for advice!
As others have answered, "date - 1" subtracts one day from the date. Here's more detail on your specific SQL snippets:
where date between to_date('2013-03-01', 'yyyy-mm-dd') and trunc(sysdate, 'mm') -1`
This evaluates to "date between 3/1/2013 and the end of last month"
TRUNC(some date, 'MM') chops the date to the beginning of the month
TRUNC(SYSDATE, 'MM') returns the beginning of the current month
TRUNC(SYSDATE, 'MM')-1 returns the last day of the previous month
trunc(months_between(date1, date2))+1
This is giving the number of full months between date1 and date2, treating any fraction of a month as a whole month. For example, if you gave it the dates 7/28/2013 and 7/29/2013 it would report one month, and it would also report one month if you gave it 7/1/2013 and 7/31/2013.
The MONTHS_BETWEEN function returns, as it implies, the number of months between two dates. The return value will have decimal places - for example a return value of 1.5 means one and a half months.
The TRUNC function, when applied against a numeric, will chop off all its decimals, so TRUNC(1.9999999) will return 1.
+1 is way to add a day to the date
-1 is way to remove a day to the date
In your specific case:
the instruction trunc(sysdate, 'mm') -1 remove one month to the date, in this case is one month before the current date.
the instruction trunc(months_between(date1, date2))+1 compute the difference in month between the two dates and then adds one.
Give a look at this SQLFiddle
it is a lazy way of adding or subtracting day(s)

day of the week in the week numbering

in oracle there is a possibility to change starting day of the week.
In US first day is SUN, and in EU - MON
This change works by changing nls_territory
However if I consider day of the week and week numbering at once, I have following situation:
day week_number
7 9 <- here it should be still week 8
1 9
2 9
3 9
4 9
5 9
6 9
7 10 <- here it should still be week 9
Do you have any idea which nls should could affect this start_day of the week?
Similar post
Get First Day Of Week From Week Number
Oracle has two date formats which give us the Week Number.
'WW' starts from the first of January and increments every seven days. As 01-JAN-2013 was a Tuesday that means the Week Number increments on a Tuesday. I guess this is the version you're using at the moment.
But there is also the 'IW' format, which is the ISO standard. In this version, the week number starts from the Monday of the week where the first of January falls. This has the peculiar side-effect of making 31-DEC-2012 the first date of Week 1 on 2013, but it does mean the week number always increments on day 1 of the week.
Find out more.
Not sure I understand your question and I see no connection between day# and week# in your example, sorry... Maybe this will help you to get ISO week. This builds annual week table. You may add 'WW' format to the query and compare dates, weeks in diff. NLS settings:
-- ISO_WK# --
SELECT mydate
, TRUNC(mydate, 'w') wk_starts
, TRUNC(mydate, 'w') + 7 - 1/86400 wk_ends
, TO_NUMBER (TO_CHAR (mydate, 'IW')) ISO_wk#
FROM
(
SELECT TRUNC(SYSDATE, 'YEAR')-1 + LEVEL AS mydate
FROM dual
CONNECT BY LEVEL <=
(
SELECT TRUNC(SYSDATE, 'YEAR')-TRUNC(ADD_MONTHS (SYSDATE, -12), 'YEAR') "Num of Days"
FROM dual
)
)
/

Timestamps and Intervals: NUMTOYMINTERVAL SYSTDATE CALCULATION SQL QUERY

I am working on a homework problem, I'm close but need some help with a data conversion I think. Or sysdate - start_date calculation
The question is:
Using the EX schema, write a SELECT statement that retrieves the date_id and start_date from the Date_Sample table (format below), followed by a column named Years_and_Months_Since_Start that uses an interval function to retrieve the number of years and months that have elapsed between the start_date and the sysdate. (Your values will vary based on the date you do this lab.) Display only the records with start dates having the month and day equal to Feb 28 (of any year).
DATE_ID START_DATE YEARS_AND_MONTHS_SINCE_START
2 Sunday , February 28, 1999 13-8
4 Monday , February 28, 2005 7-8
5 Tuesday , February 28, 2006 6-8
Our EX schema that refers to this question is simply a Date_Sample Table with two columns:
DATE_ID NUMBER NOT Null
START_DATE DATE
I Have written this code:
SELECT date_id, TO_CHAR(start_date, 'Day, MONTH DD, YYYY') AS start_date ,
NUMTOYMINTERVAL((SYSDATE - start_date), 'YEAR') AS years_and_months_since_start
FROM date_sample
WHERE TO_CHAR(start_date, 'MM/DD') = '02/28';
But my Years and months since start column is not working properly. It's getting very high numbers for years and months when the date calculated is from 1999-ish. ie, it should be 13-8 and I'm getting 5027-2 so I know it's not correct. I used NUMTOYMINTERVAL, which should be correct, but don't think the sysdate-start_date is working. Data Type for start_date is simply date. I tried ROUND but maybe need some help to get it right.
Something is wrong with my calculation and trying to figure out how to get the correct interval there. Not sure if I have provided enough information to everyone but I will let you know if I figure it out before you do.
It's a question from Murach's Oracle and SQL/PL book, chapter 17 if anyone else is trying to learn that chapter. Page 559.
you'll want MONTHS_BETWEEN in that numtoyminterval as the product of subtracting two date variables gives the answer in days which isn't usable to you and the reason its so high is you've told Oracle the answer was in years! Also use the fm modifier on the to_char to prevent excess whitespace.
select date_id,
to_char(start_date, 'fmDay, Month DD, YYYY') as start_date,
extract(year from numtoyminterval(months_between(trunc(sysdate), start_date), 'month') )
|| '-' ||
extract(month from numtoyminterval(months_between(trunc(sysdate), start_date), 'month') )
as years_and_months_since_start
from your_table
where to_char(start_date, 'MM/DD') = '02/28';
You can simplify the answer like this
SELECT date_id, start_date, numtoyminterval(months_between(sysdate, start_date), 'month') as "Years and Months Since Start"
FROM date_sample
WHERE EXTRACT (MONTH FROM start_date) = 2 AND EXTRACT (DAY FROM start_date) = 28;

Find how many days from begining of the month to current day (Oracle)

How do I determine how many days in a month, from the first to the current day (could be anywhere into the month). So if I have a field that gives 6/01/12 5:32:13 PM and 6/07/12 5:33:04 PM how do I get the difference?
I think something like this should do it:
SELECT TRUNC(SYSDATE) - TRUNC(SYSDATE,'MONTH') + 1 FROM DUAL
SELECT EXTRACT(DAY FROM sysdate) FROM dual;