Oracle SQL to_date function not working on concatenated strings - sql

I've been looking for a reason that this isn't working for quite some time. I'm concatenating two fields and attempting to run some date comparisons but it throws the error ORA-01843: not a valid month. I'm not sure what I'm doing wrong here.
Here's my code:
SELECT
sm.semester_date || cp.start_year AS effective
FROM
database.table cp,
database.table2 sm
WHERE cp.semesters_id = sm.semesters_id
AND to_date(sm.semester_date || cp.start_year, 'MM/DD/YYYY') >= to_date('06/01/2011', 'MM/DD/YYYY')
It runs fine as long as I don't add that AND statement at the end. But I need to filter the dataset.
When you run it without the filter it returns 08/15/2010 etc.
I forgot to add the mask originally when I posted this, I've corrected that. However, it still returns this error ORA-01840: input value not long enough for date format.
EDIT SOLUTION FOUND:
Firstly, thanks to everyone for helping me with this, you guys are great. Secondly, my error was being caused by a course without a start year. Very frustrating since that shouldn't be able to happen. Because the concatenated item had no year on it, it was throwing the error. I only found this because you guys helped me fix my code up. Thank you.

Try adding a separator between date and year:
SELECT
sm.semester_date || '/' || cp.start_year AS effective
FROM
database.table cp,
database.table2 sm
WHERE cp.semesters_id = sm.semesters_id
AND to_date(sm.semester_date || '/' || cp.start_year) >= to_date('06/01/2011', 'MM/DD/YYYY')
If you run the query without the filter, what does it return?

My guess is that your default date format does not match the format of the string that you are passing to TO_DATE. Add a format string as a second parameter to the call (as you've done for the second call) to specify what the format of the concatenated string is.

Is the SEMESTER_DATE column a VARCHAR2? And if so, is it a string in the format 'MM/DD/'? Or just 'MM/DD'?
As lweller points out, the first TO_DATE is also missing the format mask which would be a problem if your session's NLS_DATE_FORMAT is anything other than 'MM/DD/YYYY'.
Assuming SEMESTER_DATE is a VARCHAR2 in the format 'MM/DD', I suspect you want
AND to_date( sm.semester_date || '/' || cp.start_year, 'MM/DD/YYYY' ) >=
to_date( '06/01/2011', 'MM/DD/YYYY' )

Your first TO_DATE() relies in the system's default format. Provide a second argument with a proper format string, such as 'MM/DD/YYYY'. Please compare:
SELECT SEMESTER_DATE || START_YEAR AS BARE_STRING, TO_DATE(SEMESTER_DATE || START_YEAR) AS CASTED_TO_DATE
FROM (
SELECT '12/31' AS SEMESTER_DATE, 2010 AS START_YEAR
FROM DUAL
);
SELECT SEMESTER_DATE || START_YEAR AS BARE_STRING, TO_DATE(SEMESTER_DATE || START_YEAR, 'MM/DD/YYYY') AS CASTED_TO_DATE
FROM (
SELECT '12/31' AS SEMESTER_DATE, 2010 AS START_YEAR
FROM DUAL
);

Related

Using oracle apex dynamic action to set a date time value that is concatenated

I have a date value in one page item and i have a time value in another. i want to concat the date with the time and set it to another item using a dynamic action. Structure as below
P_DATE = '01-01-2021' (item is a date with format mask dd-mm-yyyy)
P_TIME = '08:30 AM' (item is date with format mask HH:MIAM)
Query:
select to_date(to_char(to_date(P_DATE ,'DD-MON-YYYY'), 'DD-MON-YYYY') || ' '||
to_char(P_TIME,'HH:MIAM'),'DD-MON-YYYY HH:MIAM') a
from dual;
Desired Outcome: 01-01-2021 08:30 AM
Dynamic action is on change the P_DATE item then from an sql query, concat the P_DATE and P_TIME and set it to P_VALUE
When i run the select in sql developer with hardcoded values then it returns the correct stuff but when i try to set the value in the item with the concat date it giving me invalid number error sometimes and not a valid month.
Can you suggest the corrected way or an alternative way of doing this (maybe use a function)
THank you.
You can make that much simpler, example
with test as (
select '01-01-2021' d, '08:30 PM' t
from dual)
select to_date(d||' '|| t,'MM-DD-YYYY HH:MI PM')
from test;
What do you mean by "they are date items" ? Are they of the type "Date Picker" , do they have a source column of type "DATE" in a form, or do they map to a DATE column in the database ?
In APEX, all page items are basically strings. The frontend of the web application doesn't know about oracle datatypes so everything is treated as a plain string and during the processing the conversion is done. So that is how you should treat the page items, not as DATE data type like you would in SQL or PL/SQL. To concatenate a date string and a time string, you can just a plain concatenate without TO_CHAR. This can be done in plain PL/SQL, no need to SELECT FROM DUAL - that is just an unnecessary call to the SQL engine.
This is the "true" action on the change of P_VALUE. Tested on 21.1, so depending on your version there might be some attribute naming differences but it works the same.
Action: execute server side code.
Source:
:P_VALUE := :P_DATE ||' '||:P_TIME
Items to submit: P_DATE, P_TIME.
Items to return: P_VALUE
Since you're dealing with strings there is room for error here, you'll have to ensure proper error handling if the user input does not exactly match the format since that could generate invalid date values.
From comments:
the page items are both date items
Since they are dates, you can simply use:
SELECT p_date + (p_time - TRUNC(p_time)) AS a
FROM DUAL;

Date format error

Select
(*)
From
MISC
WHERE
TO_DATE(TRANSACTION_TIME, YYYY-MM-DD HH24:MI:SS.FF6) Between
TO_DATE(:Variable1, 'YYYYMMDD') and TO_DATE(:Variable2, 'YYYYMMDD')
The transaction time column is CHAR(78) and returns 2017-06-12-12.35.37.444978 as and example. I have been struggling with this for awhile now and getting a variety of errors. With the current setup, I receive
Error Executing Query: ORA-01821: date format not recognized.
Variable1 = 20170612
Variable2 = 20170615
That's a timestamp not a date so you need to use to_timestamp() not to_date()
If you are only looking at the date, then perhaps a simpler version will work:
WHERE TO_DATE(SUBSTR(TRANSACTION_TIME, 10), 'YYYY-MM-DD') Between
TO_DATE(:Variable1, 'YYYYMMDD') and TO_DATE(:Variable2, 'YYYYMMDD')
Also, why not pass in the variables as dates rather than strings? That would get around the second conversion. If they are actually dates, then that might be where the conversion problem is.

Same query works fine in a certain database but throws error in different database

I am trying to fetch the year from 'MON-YY' format and then concatenating the fetched year with '01-JUN' . I used to_date to forcefully convert 'string' ('01-JUN-17') to 'date' type
The below code works fine in a particular database for eg db_1
select to_date('01-JUN-'||(EXTRACT(YEAR FROM to_date('MAY-17','mon-yy'))) ) AS YEAR_START_DATE from dual;
returns :
01-JUN-17
But in db_2 the same code throws the below error :
ORA-01858: a non-numeric character was found where a numeric was expected
01858. 00000 - "a non-numeric character was found where a numeric was expected"
*Cause: The input data to be converted using a date format model was
incorrect. The input data did not contain a number where a number was
required by the format model.
*Action: Fix the input data or the date format model to make sure the
elements match in number and type. Then retry the operation.
Can someone please help ?
'01-JUN-' || EXTRACT(YEAR FROM to_date('MAY-17','mon-yy'))
Will generate a string like '01-JUN-2017'
The problem occurs when you try to use TO_DATE( datestring, format_model ) without specifying a format model. In this case the query will use the NLS_DATE_FORMAT session parameter.
You can find the value for this using:
SELECT VALUE
FROM SYS.NLS_SESSION_PARAMETERS
WHERE PARAMETER - 'NLS_DATE_FORMAT';
If this does not match the format you specify then it will raise an exception.
You should explicitly specify the format model (and the language):
SELECT TO_DATE(
'01-JUN-'||EXTRACT(YEAR FROM to_date('MAY-17','mon-yy','NLS_LANGUAGE="ENGLISH"')),
'dd-MON-YYYY',
'NLS_LANGUAGE="ENGLISH"'
) AS YEAR_START_DATE
FROM DUAL;
Or you could use:
SELECT ADD_MONTHS(
TRUNC(
TO_DATE( 'MAY-17', 'MON-YY', 'NLS_LANGUAGE="ENGLISH"' ),
'YY'
),
5
) AS YEAR_START_DATE
FROM DUAL;
You are missing a date format for your first to_date('01-JUN-'..) function. The date format for one database is probably DD-MON-YYYY but it could be DD-MM-YYYY in the other database.
After adding the date format your query looks like this:
select to_date('01-JUN-' || (extract(year from to_date('MAY-17', 'mon-yy'))),'DD-MON-YYYY') as year_start_date
from dual;
It's good practise to always specify a format when converting dates from and to strings.

Problems extending `format-string` with HH24MISS when converting decimal column based timestamp to actual timestamp

tl;dr
Why am I not able to convert following string timestamp
select timestamp_format('2015-08-21 000000', 'YYYY-MM-DD HH24MISS') as timestamp
from sysibm.sysdummy1;
on an i7.1.0/OS machine?
Especially since I can convert
select timestamp_format('000000' , 'HH24MISS') as timestamp
from sysibm.sysdummy1;
to:
timestamp
-------------------------
2015-08-01 00:00:00.000000
Context
On an i7.1.0/OS machine, I have a table with timestamp data split up in several decimal columns, like
declare global temporary table tstamp
(
year dec(4,0),
month dec(2,0),
day dec(2,0),
time dec(6,0)
);
with data like
insert into session.tstamp
values (2015,8,21,92601),
(2015,8,21,132601);
on which I want to do some date filtering. Given the somewhat inflexible format, I figured that it is probably better if I convert this to a timestamp and use this to query the table. So i consulted the i/OS 7.1 Manual on timestamp_format
I started of with building the date part, ending up with
select
timestamp_format(YEAR || '-' || MONTH || '-' || DAY, 'YYYY-MM-DD') as timestamp
from session.tstamp;
which returns
TIMESTAMP
--------------------------
2015-08-21 00:00:00.000000
2015-08-21 00:00:00.000000
Perfect, let us add the time part and explicit lpad it to contain six characters:
select
timestamp_format(YEAR || '-' || MONTH || '-' || DAY || ' ' || lpad(TIME, 6, '0'), 'YYYY-MM-DD HH24MISS') as timestamp
from session.tstamp;
This results in the following error:
SQL State: 22007
Vendor Code: -20448
Message: [SQ20448] Expression not valid using format string specified for TIMESTAMP_FORMAT. Cause . . . . . : Argument 1 of the TIMESTAMP_FORMAT function can not be interpreted using the format string specified in argument 2 for one of the following reasons: -- The string expression is too short or too long. -- The string expression does not conform to the template specified in the format string. -- Too many digits were specified in the string expression for the corresponding format element in the format string. -- A value in the string expression is not valid for the corresponding format element in the format string. Recovery . . . : Specify a valid format string for the function. Try the request again.
According to the manual regarding the format-string, a separator between fields is optional:
[...]
Two format elements can optionally be separated by one or more of the following separator characters:
[...]
Question
So why are not my values accepted when using 'YYYY-MM-DD HH24MISS' as the format-string, given that I explicit has bound the time length to six characters?
Side note
It is possible to use HH24MISS on its own as format-string, so I'm not really able to wrap my head around this.
select timestamp_format(lpad(TIME, 6, '0'), 'HH24MISS') as timestamp from session.tstamp;
TIMESTAMP
--------------------------
2015-08-01 13:26:01.000000
2015-08-01 09:26:01.000000
The difficulties described, are due to defects with the TIMESTAMP_FORMAT [aka TO_DATE] scalar. The requests shown are tested to have functioned, as expected, with the DB2 for IBM i 7.3 [and as a comment to the OP suggests, also on v7r2]. I had asked a similar question, "Why the failures using my earlier examples?" in SQL convert text mm/dd/yy to date and timestamp, but I have not yet re-visited those examples on the newer release. And FWiW, there may be some updated code on IBM i 7.1 for that feature with the latest code; I do not have that level of maintenance, so I can not test if the [the last of the] enhancements that are coming for that release include the code fixes that apparently exist in newer releases.
Note that the TO_DATE feature is not a true built-in [instead, is a system-supplied User Defined Function (UDF)], so personally, I would recommend an alternative; namely, writing and using a scalar UDF specific to the task, and/or choose a more compatible and easy way to generate the TIMESTAMP from those columns as they are defined. Consider the following expression [that assumes all dates are beyond year 1000, else the expression must change to use DIGITS(YEAR) vs just YEAR]:
timestamp( YEAR concat digits( MONTH ) concat digits( DAY )
concat digits( TIME )
)
A variation of that, is to use arithmetic to achieve the same effect of a 14–character timestamp-string form 'yyyymmddhhmmss':
timestamp( concat( YEAR * 10000 + MONTH * 100 + DAY
, digits ( TIME ) ) )
The following scalar function could be created to avoid coding the expression in [VIEW] queries or other places. As coded, with nothing but an expression on a RETURN statement, should allow in-lining; I did not specify any other potentially performance-related clauses such as parallel or on-null-input:
create function y4m2d2t6TS
( year dec(4, 0)
, month dec(2, 0)
, day dec(2, 0)
, time dec(6, 0)
) returns timestamp
language sql deterministic
return
digits( YEAR ) concat digits( MONTH )
concat digits( DAY ) concat digits( TIME )
; -- this semicolon is a statement separator, not terminator of above CREATE
select
y4m2d2t6TS( year, month, day, time ) as timestamp
from session.tstamp
; -- likeness of report from above query:
TIMESTAMP
2015-08-21-09.26.01.000000
2015-08-21-13.26.01.000000
******** End of data ***
You can use this in DB2 :
values(VARCHAR_FORMAT(current_date,'YYYY-MM-DD HH24:MI:SS'))
Bye

Modify an existing to_char date format

Oracle SQL automatically converts my field D.START_DT to the following format:
TO_CHAR(D.START_DT,'YYYY-MM-DD')
Which makes it difficult for me to modify my own date format.
I've tried wrapping another TO_CHAR around it with no luck.
TO_CHAR(TO_CHAR(D.START_DT,'YYYY-MM-DD'), 'MM/DD')
And I've tried SUBSTR to select certain characters, with no luck. I think the hyphen is getting int he way.
SUBSTR(TO_CHAR(D.START_DT,'YYYY-MM-DD'), 6, 7) || '/' || SUBSTR(TO_CHAR(D.START_DT,'YYYY-MM-DD'), 9, 10)
What is the work around for this?
I agree with RMAN Express and see no problems converting dates to any format you need...
In case you still have problems try this (first to_char() in outer query is optional):
SELECT to_char(to_date(some_date, 'YYYY-MM-DD'), 'MM/DD') final_date
FROM
(
SELECT TO_CHAR(SYSDATE,'YYYY-MM-DD') some_date -- this is your "auto converted" date
FROM dual
)
/
A DATE datatype has no format. When you see a date printed on a screen, there was something that APPLIED the format you see. Could be a "default" in the program you are using (like SQL Developer) or your NLS setting, etc. But, a DATE datatype has no format. So, you have complete control over the format you see on screen.
The simplest is to use the TO_CHAR function:
select TO_CHAR(D.START_DT,'YYYY') from dual;
returns just the four digit year.
See TO_CHAR date format options.
http://docs.oracle.com/cd/E11882_01/server.112/e26088/sql_elements004.htm#CDEHIFJA
You should always supply the format in your code and not rely on some other "default" to supply it.