PL/SQL Stored Procedure in Oracle Application Express not running - oracle

I created the stored procedure which compiled with no errors. But when I run exec sp_DATE_D(2005, 2006); I receive the following error:
ERROR at line 1:
ORA-01858: a non-numeric character was found where a numeric was expected
ORA-06512: at "SP_DATE_D", line 14
ORA-06512: at line 1
I expect my sample data to look like this as an example of one row:
DATE_KEY: 01/01/2005
FULL_DATE_DESCRIPTION: Monday, January 1, 2005
The procedure should populate from this point up until the last day of the 2nd parameter.
Stored Procedure:
CREATE OR REPLACE PROCEDURE sp_DATE_DD(v_START_YEAR IN INT, v_END_YEAR IN INT) AS
v_CURRENT_DATE DATE;
v_END_DATE DATE;
BEGIN
v_CURRENT_DATE := TO_DATE('0101' || v_START_YEAR, 'MMDDYYYY');
v_END_DATE := TO_DATE('1231' || v_END_YEAR, 'MMDDYYYY');
DELETE FROM DATE_D;
WHILE v_CURRENT_DATE <= v_END_DATE
LOOP
INSERT INTO DATE_D
(
DATE_KEY,
FULL_DATE_DESCRIPTION
)
VALUES
(
TO_DATE(v_CURRENT_DATE, 'MMDDYYYY'),
TO_CHAR(v_CURRENT_DATE, 'Day, Month DD, YYYY')
);
v_CURRENT_DATE := v_CURRENT_DATE + 1;
END LOOP;
END;
/
Table Definition:
CREATE TABLE DATE_D
(
DATE_KEY DATE NOT NULL,
FULL_DATE_DESCRIPTION VARCHAR2(64) NOT NULL,
CONSTRAINT DATE_DIMENSION_PK PRIMARY KEY (DATE_KEY)
);
To add:
Can't figure how out if its a format issue or I should convert.
Solution:
Modified Line 22 -
v_CURRENT_DATE,

The problem appears to be this line:
TO_DATE(v_CURRENT_DATE, 'MMDDYYYY'),
v_CURRENT_DATE is already a DATE variable. To pass it to the TO_DATE function Oracle has to convert it to a string, and the default string format for dates is DD-MON-RR. So if v_CURRENT_DATE already contains 1-JAN-2005, this is converted to the string '01-JAN-05', which is then passed to TO_DATE. But the format argument being passed to TO_DATE is 'MMDDYYYY', which doesn't match the string it's being handed ('01-JAN-05'), and so the TO_DATE function fails.
The solution is fairly simple: since v_CURRENT_DATE is already a DATE variable there's no need to call TO_DATE at all. Change the INSERT statement to:
INSERT INTO DATE_D
(
DATE_KEY,
FULL_DATE_DESCRIPTION
)
VALUES
(
v_CURRENT_DATE,
TO_CHAR(v_CURRENT_DATE, 'Day, Month DD, YYYY')
);

Related

Using a date variable in a query to fetch records based on a given date in oracle

I need to write a function in oracle plsql that with take a date as an input and return records from a table for that particular day. If no date is given then fetch the records for current day.
Note that the column (purchase_date) is a timestamp(6) type not null column and has an index on it so I would not like to use trunc() function on the column.
Example value present in purchase_date column is --> 01-DEC-21 06.14.06.388855001 AM
create or replace FUNCTION getRecordsForDate(
input_date DATE DEFAULT SYSDATE
) RETURN sys_refcursor IS
data_out SYS_REFCURSOR;
BEGIN
OPEN data_out FOR
SELECT
p.product_name,
p.product_type,
p.purchased_by
FROM
product_details p
WHERE
AND p.purchase_date BETWEEN TO_DATE(input_date, 'DD-MON-YY')
-- AND TO_DATE('03-MAR-22 23:59:59', 'DD-MON-YY HH24:MI:SS'); --harcoded value works but I need to use input_date
AND 'TO_DATE' ||'(''' || input_date || ' 23:59:59''' ||',' || '''YYYY-MM-DD HH24:MI:SS''' ||')';
return data_out;
END getRecordsForDate;
My concatenation is not working in the last line. It gives me ORA-01858: a non-numeric character was found where a numeric was expected. Not sure what's wrong here. Would someone be able to help.
Do not use TO_DATE on a DATE.
The last line of the cursor will not work as it is a (concatenated) string literal that cannot be converted to a TIMESTAMP or a DATE.
Even if it did work (which it will not), your purchase_date is a TIMESTAMP(6) data type so you are going to exclude all value from the time 23:59:59.0000001 until 23:59:59.999999.
You want to use:
create or replace FUNCTION getRecordsForDate(
input_date DATE DEFAULT SYSDATE
) RETURN sys_refcursor
IS
data_out SYS_REFCURSOR;
BEGIN
OPEN data_out FOR
SELECT product_name,
product_type,
purchased_by
FROM product_details
WHERE purchase_date >= TRUNC(input_date)
AND purchase_date < TRUNC(input_date) + INTERVAL '1' DAY;
return data_out;
END getRecordsForDate;
/

"ORA-01861: literal does not match format string" Error in PL/SQL

Can someone help me understand what the following code line is doing wrong?
res_start_time_ := to_date(to_char(account_date_, 'YYYYMMDD ') || sched_ftime_, 'YYYYMMDD HH24:MI');
res_start_time_ and account_date_ are of DATE type.
sched_ftime_ is VARCHAR2 type and it can be NULL.
In a test scenario, I get the ORA-01861: literal does not match format string error when there is a value for account_date_ and NULL for sched_ftime_.
Can someone explain to me what I am doing wrong and how I can get rid of this error?
The source code is attempting to form a string that can be converted to a date having both a day value and a time-of-day value.
This to_char(account_date_, 'YYYYMMDD ') converts a date value into a 9 character string ending with a space which permits use of string concatenation of what should be value containing hours and minutes. Once concatenated it then attempts to convert that into a date value accurate to a minute.
However the error encountered will occur if a non-null value of sched_ftime_ isn't in a form that can be transformed to HH24:MI e.g. '123456' is to long to be interpreted as only hours and minutes.
This can be replicated:
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';
DECLARE
res_start_time_ DATE;
account_date_ DATE := TRUNC(SYSDATE);
sched_ftime_ VARCHAR2(20) := '123456'; /* this value fails */
BEGIN
res_start_time_ := to_date(
to_char(account_date_, 'YYYYMMDD ') || sched_ftime_,
'YYYYMMDD HH24:MI'
);
DBMS_OUTPUT.PUT_LINE(res_start_time_);
END;
/
ORA-01861: literal does not match format string
ORA-06512: at line 6
However a shorter value e.g. '1234' can be interpreted as hours and minutes:
DECLARE
res_start_time_ DATE;
account_date_ DATE := TRUNC(SYSDATE);
sched_ftime_ VARCHAR2(20) := '1234'; /* this value works */
BEGIN
res_start_time_ := to_date(
to_char(account_date_, 'YYYYMMDD ') || sched_ftime_,
'YYYYMMDD HH24:MI'
);
DBMS_OUTPUT.PUT_LINE(res_start_time_);
END;
/
1 rows affected
dbms_output:
2021-08-27 12:34:00
Hence I suggest you protect the conversion into a date by limiting the length of your second parameter e.g. to 4 characters perhaps using substr()
DECLARE
res_start_time_ DATE;
account_date_ DATE := TRUNC(SYSDATE);
sched_ftime_ VARCHAR2(20) := '123456'; /* this value gets truncated */
BEGIN
res_start_time_ := to_date(
to_char(account_date_, 'YYYYMMDD ')
|| substr(sched_ftime_,1,4),
'YYYYMMDD HH24:MI'
);
DBMS_OUTPUT.PUT_LINE(res_start_time_);
END;
/
You may need other validations on that varchar2 value such that they are all digits as well. Or, if you are including the colon into the hour & minutes value then the overall length needs to be 5 chars. In short you need to vet the hours and minutes so that they are logical and valid.
nb: Kudos to MT0 for the source db<>fiddle which I extended here
The source of the issue is you are allowing the time component (sched_ftime_) to assume values that you do not know the exact format before hand. You can do this, but it comes with a coding requirement. You need to establish the permissible and then derive the FORMAT SPECIFICATION at run time, and throw an error if the value does not match a permissible pattern. What is permissible and the validation is as simple or complicated as you want. As a demonstration the following function looks for 5 patterns:hh24mi, hh24miss, hh24:mi, hh24:mi:ss, and null. For those it returns the appropriate date, for anything else it returns a application defined exception.
create or replace
function get_actual_date_time( account_date_ date
, sched_ftime_ varchar2
)
return date
is
k_format_base constant varchar2(8) := 'yyyymmdd';
k_bad_time_format_msg constant varchar2(32) := ' invalid time specification.';
-- declare regexp for valid time formats
k_regx_time_hh24mm constant varchar2(7) := '^\d{4}$';
k_regx_time_hh24mmss constant varchar2(7) := '^\d{6}$';
k_regx_time_hh24mm_s constant varchar2(11) := '^\d\d:\d\d$';
k_regx_time_hh24mmss_s constant varchar2(16) := '^\d\d:\d\d:\d\d$';
-- declare actual format specification corresponding to valid format
k_fmt_time_hh24mm constant varchar2(6) := 'hh24mi';
k_fmt_time_hh24mmss constant varchar2(8) := 'hh24miss';
k_fmt_time_hh24mm_s constant varchar2(7) := 'hh24:mi';
k_fmt_time_hh24mmss_s constant varchar2(10) := 'hh24:mi:ss';
l_time_format varchar2(16);
begin
case when sched_ftime_ is null then
l_time_format := null;
when regexp_like ( sched_ftime_,k_regx_time_hh24mm) then
l_time_format := k_fmt_time_hh24mm;
when regexp_like ( sched_ftime_,k_regx_time_hh24mmss) then
l_time_format := k_fmt_time_hh24mmss;
when regexp_like ( sched_ftime_,k_regx_time_hh24mm_s) then
l_time_format := k_fmt_time_hh24mm_s;
when regexp_like ( sched_ftime_,k_regx_time_hh24mmss_s) then
l_time_format := k_fmt_time_hh24mmss_s;
else
raise_application_error( -20109,'''' || sched_ftime_ || '''' || k_bad_time_format_msg);
end case;
return to_date(to_char(account_date_,k_format_base) || sched_ftime_
, k_format_base || l_time_format);
end get_actual_date_time;
Keep in mind the above is an example only and there are many enhancement to be made. For example is will accept time specification of 99:99:99 even though it is obviously a invalid time specification. But it fits the validation for '^\d\d:\d\d:\d\d$'. Neither does it attempt to validate the valid time specification 06:45 PM. See fiddle here.

Converting an Oracle Stored Procedure DATE Input Parameter

I am very new to Oracle and have a question about input parameters to a stored procedure. Basically its a stored procedure being called from an external system passing in a date formatted as MM/DD/YYYY.
Oracle doesn't seem to like the MM/DD/YYYY format as it gives me a "not a valid month" error. (I think it wants like a DD-MMM-YYYY?) whatever the default is.
is there a way to convert the date as it comes into the procedure without getting an error?
such as:
create procedure test_proc
(
v_input_date IN DATE := to_char(v_input_date, 'MM/DD/YYYY')
)
I know the above code likely makes no actual sense but hopefully it will convey what I'd like to do. The user would call the procedure something like
BEGIN
test_proc('01/01/2018')
END
You may try with ANSI type date 'yyyy-mm-dd' formatting like in the following sample :
SQL>create or replace procedure test_proc( v_input_date date ) is
v_diff int;
begin
v_diff := trunc(sysdate)-v_input_date;
dbms_output.put_line(v_diff||' days difference...');
end;
/
SQL> set serveroutput on;
SQL> begin
test_proc(date '2018-03-21');
end;
/
2 days difference...
Your problem is not in the procedure, it is in the code calling the procedure.
'01/01/2018' is not a date it is a string but your procedure expects a date; however, Oracle tries to be helpful and will implicitly try to convert the string to a date using the TO_DATE( string_value, format_model ) function. Since it does not have a specified format model, it will use the default format for a date which is the NLS_DATE_FORMAT session parameter and if this format mask does not match the format of the string then you will get an error.
(Note: session parameters are per-user-session and can be changed by each user so you should not rely on them being the same for each user or even the same between sessions for the same user!)
You can see the format of the NLS_DATE_FORMAT session parameter using the query:
SELECT VALUE
FROM NLS_SESSION_PARAMETERS
WHERE PARAMETER = 'NLS_DATE_FORMAT';
And your code to call the procedure is implicitly being converted to something like:
BEGIN
test_proc(
TO_DATE(
'01/01/2018',
( SELECT VALUE FROM NLS_SESSION_PARAMETERS WHERE PARAMETER = 'NLS_DATE_FORMAT' )
)
);
END;
To generate a date you should explicitly convert the string to a date either by:
Using an ANSI literal
BEGIN
test_proc( DATE '2018-01-01' );
END;
Or by specifying the format mask used in the conversion
BEGIN
test_proc( TO_DATE( '01/01/2018', 'MM/DD/YYYY' ) );
END;

Formatting Date Error - Oracle 11g

Trying to write a SQL query to format a date output, but I am getting an error stating, 'a non-numeric character was found where a numeric is expected.'
Below is my SQL:
SELECT e.emp_num, emp_lname, emp_fname, sal_amount
FROM LGEMPLOYEE e
JOIN LGSALARY_HISTORY sh ON e.emp_num = sh.emp_num
WHERE sal_from = (SELECT MIN (to_date(sal_from,'dd-mon-yy'))
FROM LGSALARY_HISTORY sh
WHERE sh.emp_num = e.emp_num)
ORDER BY e.emp_num;
Can anyone help to resolve this issue?
Try to replace
MIN (to_date(sal_from,'dd-mon-yy'))
with
TO_CHAR(MIN (to_date(sal_from,'dd-mon-yy')), 'dd-mon-yy')
You're trying to compare VARCHAR2 with a DATE. Oracle uses an implicit types conversation using the following rule:
When comparing a character value with a DATE value, Oracle converts
the character data to DATE.
Just an assumption: Oracle is trying to convert sal_from to a DATE using default NLS settings (session or database) and apparently fails (because the default date format is 'dd-mm-yy' for example)
This is why it's never a good idea to store date values in a varchar2 column. There is at least one row in your table where the character string in sal_from isn't in the format you expect. That's causing the to_date call to throw an error.
One way of isolating the problematic rows would be something like
CREATE OR REPLACE FUNCTION is_valid( p_str IN VARCHAR2, p_mask IN VARCHAR2 )
RETURN VARCHAR2
IS
l_date DATE;
BEGIN
l_date := to_date( p_str, p_mask );
RETURN 'Y';
EXCEPTION
WHEN others THEN
RETURN 'N';
END;
and then
SELECT *
FROM lgsalary_history
WHERE is_valid( sal_from, 'dd-mon-yy' ) = 'N'

Character to number conversion error in trigger

How will the trigger insert between two timings? Getting character to number conversion error in the if condition while inserting. Below is my trigger.
create or replace trigger TRI_INSERT
after insert on stud_details
referencing old as old new as new
for each row
declare
strTime varchar2(20) := :new.time_stamp; -- (eg: '02/08/2013 11:09:42 PM')
begin
if (to_char(strTime, 'hh24:mi:ss') between '22:00:00' and '23:59:59') then
insert into stud_clas_details
(id,
v-id,
w-id,
al_id,
time,
Time_Stamp)
values
(seq_ve_id.nextval,
:new.vehicle_id,
:new.way_id,
'xxxx',
strTime,
sysdate);
end if;
end TRI_INSERT;
you cannot to_char() a varchar2 with a date format and expect it to work.
instead you should do
if (to_char(:new.time_stamp, 'hh24:mi:ss') between '22:00:00' and '23:59:59') then
also if you want to insert the time into the table in a specific format, use
to_char(:new.time_stamp, 'hh24:mi:ss')
as with
strTime varchar2(20) := :new.time_stamp;
you will just be inserting the date in whatever the default NLS_DATE_FORMAT is for that session (which may vary per session).
How about instead using:
if extract(hour from :new.time_stamp) in (22,23) then ...
Problem in the if condition.. Now its working fine.. Thanks for all.
create or replace trigger TRI_INSERT
after insert on stud_details
referencing old as old new as new
for each row
declare
strTime varchar2(20) := :new.time_stamp; -- (eg: '02/08/2013 11:09:42 PM')
strFromTime varchar2(20):= '22:00:00'
strToTime varchar2(20):= '23:59:59'
begin
if ((to_char(strTime, 'hh24:mi:ss') > strFromTime) and (to_char(strTime,
'hh24:mi:ss') < strToTime)) then
insert into stud_clas_details
(id,
v-id,
w-id,
al_id,
time,
Time_Stamp)
values
(seq_ve_id.nextval,
:new.vehicle_id,
:new.way_id,
'xxxx',
strTime,
sysdate);
end if;
end TRI_INSERT;

Resources