Oracle Interval Bug? - oracle

I'm using this sql query:
select sysdate, sysdate - INTERVAL '6' month from dual;
But it is return: ORA-01839: date not valid for month specified.
Which is weird, because if I change the the number into 9, it is return the date (sysdate = 31/05/11 and the subtracted is 31/08/10). I'm also tried using different value: 1,3,6,8,11 also not working, but 2,4,5,7,9,12 are working.
From the numbers, I think it is because the resulting quert doesn't have 31 days for that month. Is this the expected behavior? Because in MySQL, I can use the query (select now() - Interval 6 Month;) to get the correct value. Is there any other way?
I am using Oracle 11.1.0.6

It is the expected behaviour; see the sixth bullet in the datetime/interval arithmetic section of the documentation.
As Lisa says you can use add_months, which has the opposite behaviour - which can also cause confusion sometimes. You need to decide which is most suitable for you.

select sysdate,add_months(sysdate,-6) from dual;

Related

Date operations in Oracle

I'm trying to run this queries (Oracle 12c):
SELECT trunc(sysdate) - '25-SEP-18' FROM dual;
SELECT 1 FROM dual WHERE trunc(sysdate) = '04-SEP-19';
CREATE TABLE my_table (order_date date);
INSERT INTO my_table (order_date) VALUES ('04-SEP-19');
I expect implicit conversion and everything is good with the 2 last queries, but for the first i get error ORA-01722: invalid number. NLS_DATE_FORMAT = 'DD-MON-RR'. What is the problem?
The question is WHY is does not work? I didn't find any explanations in documentation.
The documentation has a section on Datetime/Interval Arithmetic which explains what is allowed. The table shows that arithmetic is only allowed between dates, timestamp, intervals and numbers. When you do:
SELECT trunc(sysdate) - '25-SEP-18'
you are trying to subtract a string from a date, which isn't possible. Oracle 'helpfully' tries anyway and interprets the string as a number, effectively doing:
SELECT trunc(sysdate) - to_number('25-SEP-18')
which understandably throws the error you see, "ORA-01722: invalid number". As already said, you should explicitly convert your string to a date:
SELECT trunc(sysdate) - to_number('25-SEP-18', 'DD-MON-RR')
or preferably with a four-digit year, and since you're using a month name it's safer to specify the language that is in:
SELECT trunc(sysdate) - to_number('25-SEP-2018', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE=ENGLISH')
or more simply, if it's a fixed value, with a date literal:
SELECT trunc(sysdate) - DATE '2018-09-25'
I expect implicit conversion
You should not rely on implicit conversion, particularly where that is influenced by session NLS settins. As well as the date language I already mentioned, someone else running your statement could have a different NLS_DATE_FORMAT setting which could lead to errors or more subtle data mismatches or corruption; e.g.
alter session set nls_date_format = 'DD-MON-YYYY';
SELECT trunc(sysdate) - DATE '2018-09-25' FROM dual;
TRUNC(SYSDATE)-DATE'2018-09-25'
-------------------------------
344
SELECT trunc(sysdate) - to_date('25-SEP-18') FROM dual;
TRUNC(SYSDATE)-TO_DATE('25-SEP-18')
-----------------------------------
730831
SELECT 1 FROM dual WHERE trunc(sysdate) = '04-SEP-19';
no rows selected
CREATE TABLE my_table (order_date date);
INSERT INTO my_table (order_date) VALUES ('04-SEP-19');
The second query gets a much bigger value than expected; and the third gets no rows back from dual.
Looking at the implicitly converted date shows you why:
SELECT to_char(order_date, 'SYYYY-MM-DD HH24:MI:SS') FROM my_table;
TO_CHAR(ORDER_DATE,'
--------------------
0019-09-04 00:00:00
With a YYYY mask (and no FX modifier) a 2-digit year value like 19 is converted as 0019, not 2019. That sort of problem could go unnoticed for some time, giving you incorrect results in the meantime.
If the session's format mask had RRRR or - as you have - RR then it would be interpreted as 2019; but the point is that you usually have no control over the settings in another session that runs your code later.
You can also cause performance issues or errors by creating implicit conversions where you didn't expect, or where they behave in a way you didn't expect. Not in this example - "When comparing a character value with a DATE value, Oracle converts the character data to DATE" - but it still comes up. It's better to avoid the possibility.
When dealing with strings with dates in them you should use the to TO_DATE command, otherwise Oracle may not always figure out that the string contains a date.
SELECT trunc(sysdate) - TO_DATE('25-SEP-18') FROM dual;
Even better is to indicate the format of the date within the string
SELECT trunc(sysdate) - TO_DATE('25-SEP-18','DD-MON-RR') FROM dual;

Why am I receiving an error for TRUNC function?

select TRUNC(TO_DATE('22-AUG-03'), 'YEAR')
from dual;
ORA-01843: not a valid month
1st example -> https://www.techonthenet.com/oracle/functions/trunc_date.php
I know "trunc" function takes in a date and optional fmt parameter.
Why am I getting this error?
I don't think the problem is trunc(). I think the problem is the date format. You are safer using the date keyword and an ISO-standard formatted date:
select TRUNC(DATE '2003-08-22', 'YEAR')
from dual;
The interpretation of a date string depends on the internationalization settings for your particular environment. The above does not have that dependency.
Here is a very simple check: IN THE SAME SESSION where your SELECT fails, and WITHOUT you altering the NLS_DATE_FORMAT, see what happens if you run the simpler statement,
SELECT TO_DATE('22-AUG-03') FROM DUAL
You will get the same error - which proves conclusively that it has nothing to do with TRUNC().
To make everything work exactly as in the tutorial, issue the command
ALTER SESSION SET NLS_DATE_FORMAT = 'dd-MON-yy'
first, before everything else. Note though that formats that have the year as two digits instead of four are a very bad idea in most cases.
It seems that you want to get the year of the date you receive in parameter
If you get '22-AUG-03' as parameter, add to TO_DATE function the date format you expect and then trunc it:
SELECT TRUNC(TO_DATE('22-AUG-03', 'dd-MON-yy'),'YEAR') FROM DUAL
Use SELECT EXTRACT(YEAR FROM TO_DATE('22-AUG-03', 'dd-MON-yy')) FROM DUAL;
to extract year from date.

Date as number in sql

When we type current date in Excel cell as 08-May-2013
Right click on the cell and in the format when i click number as category i get a number
Date-08-May-13
Formatted one-41402.00
So is there anyway i can get the same number in sql
I tried using this.
select to_char(sysdate,'J') from dual
But the output is 2456421
I understand that this is a Julian value
But can anyone help me in getting the output as that i am getting on excel i.e; 41402
The Windows version of Excel stores dates as serial numbers. 01-Jan-1900 is 1, 02-Jan-1900 is 2, etc. The Mac version used to use a different starting date; I don't know whether that's still the case.
The essential data you need is in simple date arithmetic.
select current_date, current_date - date '1900-01-01'
from dual;
That returns 41400.67037037037 for my current connection. Rounding up and adding 1 for fenceposting would return the number you're looking for, but I'd want to test that with multiple time zones and such before I'd swear by it.
A date in Excel is stored as a serial number, with 01-JAN-1900 as 1. Citation.
We can do arithmetic with dates in Oracle, so converting to an Excel date from Oracle would be:
trunc(sysdate) - to_date( '1900-01-01', 'yyyy-mm-dd')
I've tested this and infuriatingly it produces 41401 - because it's going from midnight. So obviously Microsoft are using a ceiling function to raise it to the next integer:
ceil (sysdate - to_date( '1900-01-01', 'yyyy-mm-dd') )

Oracle date corruption during update

I'm migrating some data from one oracle schema/table to a new schema/table on the same database.
The migration script does the following:
create table newtable as select
...
cast(ACTIVITYDATE as date) as ACTIVITY_DATE,
...
FROM oldtable where ACTIVITYDATE > sysdate - 1000;
If I look at the original data, it looks fine - here's one record:
select
activitydate,
to_char(activitydate, 'MON DD,YYYY'),
to_char(activitydate, 'DD-MON-YYYY HH24:MI:SS'),
dump(activitydate),
length(activitydate)
from orginaltable where oldpk = 1067514
Result:
18-NOV-10 NOV 18,2010 18-NOV-2010 12:59:15 Typ=12 Len=7: 120,110,11,18,13,60,16
The migrated data, showing that the data is corrupt:
select
activity_date,
to_char(activity_date, 'MON DD,YYYY'),
to_char(activity_date, 'DD-MON-YYYY HH24:MI:SS'),
dump(activity_date),
length(activity_date)
from newtable
where id = 1067514
Result:
18-NOV-10 000 00,0000 00-000-0000 00:00:00 Typ=12 Len=7: 120,110,11,18,13,0,16
Around 5000 out of 350k records show this problem.
Can anyone explain how this happened?
UPDATE:
I don't find any published reference to this specific type of DATE corruption on the Oracle support site. (It may be there, my quick searches just didn't turn it up.)
Baddate Script To Check Database For Corrupt dates [ID 95402.1]
Bug 2790435 - Serial INSERT with parallel SELECT and type conversion can insert corrupt data [ID 2790435.8]
The output from the DUMP() function is showing the date value is indeed invalid:
Typ=12 Len=7: 120,110,11,18,13,0,16
We expect that the minutes byte should be a value between one and sixty, not zero.
The 7 bytes of a DATE value represent, in order, century(+100), year(+100), month, day, hour(+1), minutes(+1), seconds(+1).
The only time I have seen invalid DATE values like this when a DATE value was being supplied as a bind variable, from a Pro*C program (where the bind value is supplied in the internal 7 byte representation, entirely bypassing the normal validation routines that catch invalid dates e.g. Feb 30)
There is no reason to expect the behavior you're seeing, given the Oracle syntax you posted.
This is either a spurious anomaly (memory corruption?) or if this is repeatable, then it's a flaw (bug) in the Oracle code. If it's a flaw in the Oracle code, the most likely suspects would be "newish" features in an un-patched release.
(I know CAST is a standard SQL function that's been around for ages in other databases. I guess I'm old school, and have never introduced it into my Oracle-syntax repertoire. I don't know what version of Oracle it was that introduced the CAST, but I would have stayed away from it in the first release it appeared in.)
The big 'red flag' (that another commenter noted) is that CAST( datecol AS DATE).
You would expect the optimizer to treat that as equivalent to date_col ... but past experience shows us that TO_NUMBER( number_col ) is actually interpreted by the optimizer as TO_NUMBER( TO_CHAR ( number_col ) ).
I suspect something similar might be going on with that unneeded CAST.
Based on that one record you showed, I suspect the issue is with values with a "59" value for minutes or seconds, and possibly a "23" value for hours, would be the ones that show the error.
I would try checking for places where the minutes, hour or seconds are stored as 0:
SELECT id, DUMP(activitydate)
FROM newtable
WHERE DUMP(activitydate) LIKE '%,0,%'
OR DUMP(activitydate) LIKE '%,0'
I've seen similar things to spence7593, again with Pro*C.
It is possible to create invalid dates programmatically using a DBMS_STATS package.
Not sure if there is a similar mechanism to reverse that.
create or replace function stats_raw_to_date (p_in raw) return date is
v_date date;
v_char varchar2(25);
begin
dbms_stats.CONVERT_RAW_VALUE(p_in, v_date);
return v_date;
exception
when others then return null;
end;
/
select stats_raw_to_date(utl_raw.cast_to_raw(
chr(120)||chr(110)||chr(11)||chr(18)||chr(13)||chr(0)||chr(16)))
from dual;

Sybase equivalent in Oracle

I am doing a change of code from Sybase to Oracle.
I have problem in converting the below query to oracle.
Select Custodian_addr,convert(datetime,dateadd(ss,CreateDT,"01/01/1970")
Here CreateDT is the column name whose value for instance is 1015015173
The result for date conversion (for this example)is March 1 2002 8:39 PM GMT
I researched and found an oracle alternative which results in error
Select Custodian_addr,to_char(CreateDT,"SS")
I am getting a query error in Oracle.I am not able to identify whats wrong. Since I am executing this in Perl ["] has to escaped or what might be the issue? Please suggest me a solution
SELECT 'some address' as custodian_addr,
date '1970-01-01' + 1015015173/86400 as create_dt
from dual
/
CUSTODIAN_AD CREATE_DT
------------ -------------------
some address 2002-03-01 20:39:33
Oracle date arithmetic is pretty simple -- adding 1 to a date increments it by 1 day. So since that number is seconds, dividing it by 86400 (60*60*24) casts that number as a number of days (and fractions thereof).
What is the error you get?
Your TO_CHAR function should not use double quotes. It needs to be
TO_CHAR(CreateDT,'SS')
If you put something in double quotes in Oracle, it's interpreted as an identifier, not a string constant.

Resources