I am using Application Express (OracleXE 11g Express Edition installed on Win7 32-bit) on-line and the below stored procedure compiles and executes successfully with no errors. When compiling in SQL Plus command line, code compiles successfully, but when I execute, it gives me an error. Now I already solved the error (code snippet shown below), my question is why doesn't the database engine in Application Express throw an error ? Hope I am explaining my question correctly.
Table definition
CREATE TABLE DATE_DIMENSION
(
DATE_KEY DATE NOT NULL,
FULL_DATE_DESCRIPTION VARCHAR2(64) NOT NULL,
DAY_OF_WEEK NUMBER(1,0) NOT NULL,
DAY_OF_MONTH NUMBER(2,0) NOT NULL,
DAY_OF_YEAR NUMBER(3,0) NOT NULL,
LAST_DAY_OF_WEEK_INDICATOR CHAR(1) NOT NULL,
LAST_DAY_OF_MONTH_INDICATOR CHAR(1) NOT NULL,
WEEK_ENDING_DATE DATE NOT NULL,
MONTH_NUMBER NUMBER(2,0) NOT NULL,
MONTH_NAME VARCHAR2(32) NOT NULL,
YEAR_MONTH CHAR(32) NOT NULL,
QUARTER_NUMBER NUMBER(1,0) NOT NULL,
YEAR_QUARTER CHAR(32) NOT NULL,
YEAR_NUMBER NUMBER(4,0) NOT NULL,
CONSTRAINT DATE_DIMENSION_PK PRIMARY KEY (DATE_KEY)
)
/
Stored Procedure
create or replace PROCEDURE sp_DATE_DIMENSION(v_START_YEAR IN INT, v_END_YEAR IN INT) AS
--Declare two variables as DATE datatypes
v_CURRENT_DATE DATE;
v_END_DATE DATE;
BEGIN
--Assign the start year and end year to it's respective variables
v_CURRENT_DATE := TO_DATE('0101' || v_START_YEAR, 'MMDDYYYY');
v_END_DATE := TO_DATE('1231' || v_END_YEAR, 'MMDDYYYY');
--Clear/Dump what is currently stored in the table
DELETE FROM DATE_DIMENSION;
--Check the condition to see if the start year is less than the end year (Input Parameters)
WHILE v_CURRENT_DATE <= v_END_DATE
LOOP
--DATE_DIMENSION Table
INSERT INTO DATE_DIMENSION
(
DATE_KEY,
FULL_DATE_DESCRIPTION,
DAY_OF_WEEK,
DAY_OF_MONTH,
DAY_OF_YEAR,
LAST_DAY_OF_WEEK_INDICATOR,
LAST_DAY_OF_MONTH_INDICATOR,
WEEK_ENDING_DATE,
MONTH_NUMBER,
MONTH_NAME,
YEAR_MONTH,
QUARTER_NUMBER,
YEAR_QUARTER,
YEAR_NUMBER
)
VALUES
(
v_CURRENT_DATE, --DATE_KEY
TO_CHAR(v_CURRENT_DATE, 'Day, Month DD, YYYY'), --FULL_DATE_DESCRIPTION
TO_NUMBER(TO_CHAR(v_CURRENT_DATE, 'D')) -1, --DAY_OF_WEEK
TO_CHAR(v_CURRENT_DATE,'DD'), --DAY_OF_MONTH
TO_CHAR(v_CURRENT_DATE,'DDD'), --DAY_OF_YEAR
CASE --LAST_DAY_OF_WEEK_INDICATOR
WHEN TO_CHAR(v_CURRENT_DATE,'FMDay') = 'Saturday' THEN 'Y'
ELSE 'N'
END,
CASE --LAST_DAY_OF_MONTH_INDICATOR
WHEN LAST_DAY(TO_DATE(v_CURRENT_DATE, 'MM/DD/YYYY')) = TO_DATE(v_CURRENT_DATE, 'MM/DD/YYYY') THEN 'Y'
ELSE 'N'
END,
CASE --WEEK_ENDING_DATE OF CURRENT WEEK ENDING ON SATURDAY
WHEN TO_CHAR(v_CURRENT_DATE,'FMDay') = 'Saturday' THEN v_CURRENT_DATE
ELSE NEXT_DAY(v_CURRENT_DATE,'SATURDAY')
END,
TO_CHAR(v_CURRENT_DATE,'MM'), --MONTH_NUMBER
TO_CHAR(v_CURRENT_DATE,'MONTH'), --MONTH_NAME
TO_CHAR(v_CURRENT_DATE,'MONTH YYYY'), --YEAR_MONTH
TO_CHAR(v_CURRENT_DATE,'Q'), --QUARTER_NUMBER
TO_CHAR(v_CURRENT_DATE,'YYYY Q'), --YEAR_QUARTER
TO_CHAR(v_CURRENT_DATE,'YYYY') --YEAR_NUMBER
);
--Increment and assign the current date value to be re-evaluated
v_CURRENT_DATE := v_CURRENT_DATE + 1;
END LOOP;
END;
FYI - This piece of code solved my issue to make it execute using SQL Plus.
CASE --LAST_DAY_OF_MONTH_INDICATOR
WHEN LAST_DAY(v_CURRENT_DATE) = v_CURRENT_DATE THEN 'Y'
ELSE 'N'
END,
You're doing this:
WHEN LAST_DAY(TO_DATE(v_CURRENT_DATE, 'MM/DD/YYYY'))
= TO_DATE(v_CURRENT_DATE, 'MM/DD/YYYY') THEN 'Y'
But v_current_date is already a DATE type, so for both of those calls to to_date, you're really doing to_date(to_char(v_current_date), 'MM/DD/YYYY'), and the to_char is using your session NLS_DATE_FORMAT - which is presumably MM/DD/YYYY in Apex, but something else in SQL*Plus.
You haven't shown your actual error, so I'm speculating a little, but you're effectively doing something like:
to_date(to_char(v_current_date, 'DD/MM/YYYY'), 'MM/DD/YYYY')
That would work sometimes, but get an invalid month error if the day of the month is after the 12th, since it's transposing the month and day numbers. Or your NLS setting mat be using MON, which would get the same error as Oracle is quite forgiving about using names instead of month numbers. Or some other format which gives a different error - there are several you could hit.
Your fix, to just use WHEN LAST_DAY(v_CURRENT_DATE) = v_CURRENT_DATE, avoids both the explicit conversion to a date, and the implicit conversion from a date to string, so there is no impact from your NLS settings.
It sounds like you are saying that neither environment threw a compilation error but one environment threw a runtime error. I would guess in that case that you've written code that depends on environmental settings that are different in the two environments.
Looking at your LAST_DAY_OF_WEEK_INDICATOR, that is in fact what you did by passing a DATE to TO_DATE. Functionally, that doesn't make sense, to_date does not take a DATE as a parameter, it only accepts a VARCHAR2. When you call
TO_DATE(v_CURRENT_DATE, 'MM/DD/YYYY')
therefore, Oracle has to do a few things.
First, it implicitly casts v_current_date to a string. Because it is an implicit cast, it will use your session's NLS_DATE_FORMAT setting. Every session in a database may have a different NLS_DATE_FORMAT and the NLS_DATE_FORMAT for a single session can change over time so the behavior of this implicit cast is not known at compile time. If your NLS_DATE_FORMAT is DD-MON-RR, which is the default setting if you did an Oracle client install on an English language Windows machine, the string that is passed to to_date would be "20-FEB-15" (assuming you called the procedure today). If your NLS_DATE_FORMAT is DD/MM/YYYY, the string passed to to_date would be "20/02/2015".
Next, it calls to_date passing in the string that was just generated and the format mask that you specified. If you happen to be in a session where the NLS_DATE_FORMAT matches the format mask you passed to to_date, you'll get the same date back from to_date. If there is a mismatch, however, you may get an error (a string in the format DD-MON-YYYY will never convert to a valid date in the MM/DD/YYYY format) or you may get an unexpected result (a string in the format DD/MM/YYYY may convert to a valid date using the MM/DD/YYYY format mask but that date won't be the same one that you started with-- March 1 would be converted to January 3 for example).
If you want your code to run correctly regardless of the environment, avoid implicit casts.
Related
How can we change the date format from DD-MON-YYYY to this format YYYY-MM-DD.
I have a date type column in a table. I want to display that value of that date column in this format - YYYY-MM-DD.
I tried with this -
disp_date := to_char(to_date(disp_date,'dd-mm-rrrr'),'rrrr-mm-dd')
and
disp_date := to_char(to_date(disp_date,'dd-mm-yyyy'),'yyyy-mm-dd')
While executing the above I got an error message stating that:
ORA-01861 Literal does not match format string
Please note the below details of my system,
select value from v$nls_parameters where parameter = 'NLS_DATE_LANGUAGE';
--AMERICAN
select value from v$nls_parameters where parameter = 'NLS_DATE_FORMAT';
--DD-MON-RRRR
If column's datatype is DATE - which is what your sentence suggests:
I have a date type column in a table
then you don't to_date it - it already is a date. Just apply to_char with desired format mask, e.g.
select to_char(disp_date, 'yyyy-mm-dd') from your_table
If you want to change the default display format then run
alter session set nls_date_format = 'YYYY-MM-DD';
select disp_date from ...
Note, your client application may change the format again according to settings in this client application.
A date does not have a format - it is stored internally to the database as a binary value using 7-bytes (representing century, year-of-century, month, day, hour, minute and second). It is not until whatever user interface you are using (i.e. SQL/Plus, SQL Developer, Java, etc.) tries to display it to you, the user, that is is converted it into something you would find meaningful (usually a string) that the date is formatted (and that conversion is done by the user interface and not by the database).
How can we change one date format to another format in oracle?
Since a date does not have a format then this question does not make sense.
If instead, you ask:
How can we display a date in a format in oracle?
If you want to display the date with a specific format then you want to explicitly convert it from a date to a string using TO_CHAR (rather than relying on an implicit conversion by the user interface). Since it is already a DATE then you do not need to use TO_DATE on it and can just use:
DECLARE
date_value DATE := SYSDATE;
formatted_date VARCHAR2(10);
BEGIN
formatted_date := TO_CHAR(date_value, 'yyyy-mm-dd');
DBMS_OUTPUT.PUT_LINE( formatted_date );
END;
/
Now, if your disp_date variable is a string (and not a date) then your code works:
DECLARE
disp_date VARCHAR2(11) := TO_CHAR(SYSDATE, 'DD-MON-RRRR');
BEGIN
disp_date := TO_CHAR(TO_DATE(disp_date, 'DD-MON-RRRR'), 'yyyy-mm-dd');
DBMS_OUTPUT.PUT_LINE( disp_date );
END;
/
db<>fiddle here
I have this query in oracle:
DELETE FROM my_table
WHERE to_date(last_update, 'DD/MM/YYYY') < to_date('01/01/2000', 'DD/MM/YYYY');
when I run this, I get this error:
ORA-01841: (full) year must be between -4713 and +9999 and must not be 0
there is not any 0 value in the table.
any one knows what is the problem?
I am assuming that you have stored your dates as a string with the DD/MM/YYYY format; it would be better if you stored them all as a DATE data type and then you would not have to do this conversion (and you would be using the most appropriate data type for the data).
From Oracle 12, you can use:
SELECT *
FROM my_table
WHERE TO_DATE( last_update, 'DD/MM/YYYY' DEFAULT NULL ON CONVERSION ERROR ) IS NULL;
To identify the rows that are raising that exception.
If you are already storing them as a DATE data type then don't use TO_DATE on a value that is already a DATE as TO_DATE expects a string so Oracle will implicitly cast your DATE to a string and then try to convert it back and your query is effectively:
DELETE FROM my_table
WHERE TO_DATE(
TO_CHAR(
last_update,
( SELECT value FROM NLS_SESSION_SETTINGS WHERE PARAMETER = 'NLS_DATE_FORMAT' )
),
'DD/MM/YYYY'
) < to_date('01/01/2000', 'DD/MM/YYYY');
And if the NLS_DATE_FORMAT and your format model do not match then you will get errors (or, worse, the query will succeed and your data will be inconsistent as it may have swapped days and months or months and years).
Instead, just use:
DELETE FROM my_table
WHERE last_update < DATE '2000-01-01';
If the datatype of last_update is date, don't use the to_date function:
DELETE FROM my_table
WHERE last_update < to_date('01/01/2000', 'DD/MM/YYYY');
I have table that contain field 'EVT_START_DATE' with datatype is DATE on Oracle.
I want to insert the current date into this field, with french format dd/mm/yyyy.
I did this command line:
ALTER SESSION SET NLS_LANGUAGE = 'FRENCH';
After that, i tried this block of PL/SQL code:
DECLARE
v_evt_num VARCHAR(200);
BEGIN
v_evt_num := 'PC_' || evt_seq.NEXTVAL;
INSERT INTO event
(EVT_NUM,EVT_CODE_PAY,EVT_CODE_USER,EVT_START_DATE,EVT_MT)
VALUES
(v_evt_num,
'129',
'247',
TO_DATE(SYSDATE, 'DD/MM/YYYY'),
:i_mt);
COMMIT;
dbms_output.put_line('Success.');
EXCEPTION WHEN OTHERS THEN
dbms_output.put_line('Fail.');
END;
But I get fail message.
If EVT_START_DATE is of type DATE, then use just:
INSERT INTO event
(EVT_NUM,EVT_CODE_PAY,EVT_CODE_USER,EVT_START_DATE,EVT_MT)
VALUES
(v_evt_num,
'129',
'247',
SYSDATE,
:i_mt);
whithout usingto_date function.
Oracle's SYSDATE function returns value of type DATE, so there is no need to use to_date in order to convert it to DATE, bacause it is DATE yet.
SYSDATE returns the current date and time set for the operating system
on which the database resides. The datatype of the returned value is
DATE
CREATE OR REPLACE PROCEDURE PROC1(
V_STARTTIME IN TIMESTAMP ,
V_ENDTIME IN TIMESTAMP )
BEGIN
INSERT INTO TAB1
SELECT COINS FROM TAB2
WHERE DATE BETWEEN TO_DATE(V_STARTTIME,'mm/dd/yyyy hh:mi:ss aM') AND TO_DATE(V_ENDTIME ,'mm/dd/yyyy hh:mi:ss aM');
END;
SAMPLE DATE in Tab2 5/5/2014 9:46:38.000000 AM
My script runs between a range of dates. The two dates are IN parameters.
When I execute the procedure
Execute proc1('5/05/2014 11:25:00 AM','5/05/2014 12:25:00 PM')
I am getting not a valid month error.
Any idea how to fix this?
Thanks
Your procedure takes parameters of type timestamp. You're actually passing parameters of type varchar2 in your call. That forces Oracle to perform implicit conversion of the varchar2 parameters to timestamp using your session's NLS_TIMESTAMP_FORMAT. That will likely be different for different sessions so it is likely that at least some sessions will get an error because the string doesn't match the format of that session's NLS_TIMESTAMP_FORMAT. You'd be much better served passing in an actual timestamp either by explicitly calling to_timestamp or by passing a timestamp literal.
Your procedure then takes the timestamp parameters and pass them to the to_date function. The to_date function does not take parameters of type timestamp, it only takes parameters of type varchar2. That forces Oracle to do another implicit conversion of the timestamp parameters to varchar2, again using the session's NLS_TIMESTAMP_FORMAT. If the session's NLS_TIMESTAMP_FORMAT doesn't match the explicit format mask in your to_date call, you'll get an error or the conversion will return a result that you don't expect.
If the column in your table is actually of type date, you can directly compare a date to a timestamp. So there doesn't appear to be any reason to call to_date here. Based on your sample data, though, it appears that the column in your table is actually of type timestamp rather than date as your code implies, since a date does not have fractional seconds of precision. If that's the case, it makes even less sense to call to_date in your SELECT statement since your parameters are actually of type timestamp and your column is of type timestamp. Just compare the timestamp values.
My guess, therefore, is that you want something like
CREATE OR REPLACE PROCEDURE PROC1(
V_STARTTIME IN TIMESTAMP ,
V_ENDTIME IN TIMESTAMP )
BEGIN
INSERT INTO TAB1( <<column name>> )
SELECT COINS
FROM TAB2
WHERE <<timestamp column name>> BETWEEN v_starttime AND v_endtime;
END;
and that you want to cal the procedure by passing actual timestamps. Using timestamp literals
Execute proc1(timestamp '2014-05-05 11:25:00', timestamp '2014-05-05 12:25:00' )
or by explicitly calling to_timestamp
execute proc1( to_timestamp( '5/05/2014 11:25:00 AM', 'MM/DD/YYYY HH:MI:SS AM' ),
to_timestamp( '5/05/2014 12:25:00 PM', 'MM/DD/YYYY HH:MI:SS AM' ) );
That should get rid of all the implicit type conversions that are currently taking place.
Form the client I need to pass a SYSDATE argument to PL/SQL. In the server it need to be converted to date, for which iam using TO_DATE(in_timestamp, 'DD-MON-YYYY HH24:MI:SS'); What should be the data type of in_timestamp?
SYSDATE is itself a date, and it seems like the target field is also a date (Since you used TO_DATE()). Thus you don't actually need a conversion here.
Just pass SYSDATE and use it in your PL/SQL block, meaning in_timestamp should be a date.
As far as TO_DATE is concerned you can have CHAR, VARCHAR or VARCHAR2(recommended) basically it should be of String type as following examples suggest:-
to_date('2003/07/09', 'yyyy/mm/dd') would return a date value of July 9, 2003.
to_date('070903', 'MMDDYY') would return a date value of July 9, 2003.
to_date('20020315', 'yyyymmdd') would return a date value of Mar 15, 2002.
You can find more information related to TO_DATE at this link,
EDIT
*"However, if you are passing sysdate you dont't need to use TO_DATE again because it is already a Date value..."*as mentioned by #Gaurav and Hence the dataType of in_timestamp should be DATE..