How to check time format in Oracle - oracle

I need to check that my string is in a format of time HH24:MI:SS.
I made a function
CREATE OR REPLACE FUNCTION check_time(myStr VARCHAR2) RETURN INT IS
p_temp DATE;
BEGIN
p_temp := TO_DATE(myStr, 'HH24:MI:SS');
RETURN 1;
EXCEPTION WHEN others THEN RETURN 0;
END;
But the TO_DATE() not failing when getting number between 0 and 23;
I need the string to be in the exact format of HH24:MI:SS with the colon.
I looking for a solution that uses the Oracle Date/Time formats.

If you want to prevent Oracle from apply lax rules to the conversion then you can add the 'FX' format modifier:
p_temp := TO_DATE(myStr, 'FXHH24:MI:SS');
On recent versions of Oracle you don't need your own function, you can use validate_conversion(), which will give you the same 0/1 result:
validate_conversion('<your_string>' as date, 'FXHH24:MI:SS')
fiddle with some sample valid and invalid values.

Related

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;

How to convert CLOB to UTF8 in an Oracle query?

I've got a query with a CLOB field which I want to return her value in UTF8 format. The next query works fine if the field are varchar, for example, but if it is CLOB doesn't return a correct UTF8 string.
select convert(field, 'AL32UTF8', 'WE8ISO8859P15') from table;
How can I do to return a UTF8 string from a CLOB in a query?
Use DBMS_LOB.CONVERTTOBLOB.
From the oracle documentation:
Oracle discourages the use of the CONVERT function in the current
Oracle Database release. The return value of CONVERT has a character
datatype, so it should be either in the database character set or in
the national character set, depending on the datatype. Any
dest_char_set that is not one of these two character sets is
unsupported. …
If you need a character datatype like CLOB in a character set that differs from those the database is setup with it should be converted into a BLOB.
This is where DBMS_LOB.CONVERTTOBLOB comes in.
If you need a function that returns a BLOB you have to wrap CONVERTTOBLOB into your own function.
For example:
CREATE OR REPLACE FUNCTION clob_to_blob (p_clob CLOB, p_charsetname VARCHAR2)
RETURN BLOB
AS
l_lang_ctx INTEGER := DBMS_LOB.default_lang_ctx;
l_warning INTEGER;
l_dest_offset NUMBER := 1;
l_src_offset NUMBER := 1;
l_return BLOB;
BEGIN
DBMS_LOB.createtemporary (l_return, FALSE);
DBMS_LOB.converttoblob (
l_return,
p_clob,
DBMS_LOB.lobmaxsize,
l_dest_offset,
l_src_offset,
CASE WHEN p_charsetname IS NOT NULL THEN NLS_CHARSET_ID (p_charsetname) ELSE DBMS_LOB.default_csid END,
l_lang_ctx,
l_warning);
RETURN l_return;
END;
This allows queries like:
SELECT clob_to_blob (field, 'UTF8') FROM t;
To get a list of supported values for the character set name use:
SELECT *
FROM v$nls_valid_values
WHERE parameter = 'CHARACTERSET'
use dbms_lob package for it
for example
select convert(dbms_lob.substr(field,dbms_lob.getlength(field), **0**),
'AL32UTF8',
'WE8ISO8859P15')
from table;
Fixed it:
select convert(dbms_lob.substr(field,dbms_lob.getlength(field)),
'AL32UTF8',
'WE8ISO8859P15')
from table;

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'

Error while receiving date as parameter input

I am getting an error when I plug in a date to test a parameter I am working on. The error is:
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.
Here is my test script:
set serveroutput on
declare
type tempcursor is ref cursor;
v_cur_result tempcursor;
errcode number;
errmesg varchar2(1000);
p_statusmnemonic_in acts.ct_cu_act_medrecon_pg.varchararrayplstype;
p_processtypemnemonic_in transactionprocesslog.processtypemnemonic%type;
p_primarymemberplanid_in membermedicalreconcilationhdr.primarymemberplanid%type;
p_assigneduserid_in membermedicalreconcilationhdr.assigneduserid%type;
p_accountorgid_in membermedicalreconcilationhdr.accountorgid%type;
p_reconstatusmnemonic_in membermedicalreconcilationhdr.reconciliationstatusmnemonic%type;
p_estimatedenddt_in membermedicalreconcilationhdr.estimatedenddt%type;
p_actualenddt_in membermedicalreconcilationhdr.actualenddt%type;
p_inserteddate_in membermedicalreconcilationhdr.inserteddt%type;
p_insertedby_in membermedicalreconcilationhdr.insertedby%type;
p_updateddate_in membermedicalreconcilationhdr.updateddt%type;
p_updatedby_in membermedicalreconcilationhdr.updatedby%type;
begin
p_statusmnemonic_in(1) := ('OPEN');
p_statusmnemonic_in(2) := ('SUSPENDED_PRIOR_TO_COMPARE');
ct_cu_act_medrecon_pg.sps_get_patientmedrecs_hdr
(p_statusmnemonic_in,'NO','26-JAN-14',v_cur_result, errcode, errmesg);
loop
fetch v_cur_result into p_primarymemberplanid_in,p_assigneduserid_in,p_accountorgid_in,p_reconstatusmnemonic_in,
p_estimatedenddt_in,p_actualenddt_in,p_inserteddate_in,p_insertedby_in,
p_updateddate_in,p_updatedby_in,p_processtypemnemonic_in;
dbms_output.put_line(' planid '||p_primarymemberplanid_in||' userid '||p_assigneduserid_in);
exit when v_cur_result%notfound;
end loop;
dbms_output.put_line(' error code '||errcode||' message '||errmesg);
end;
I dont get an error when the date is set to '24-JAN-13' but the second I change anything I get that error. Here are the two estimated date fields in the table I am looking at:
24-JAN-13 04.29.19.989847000 PM
28-JAN-13 08.52.27.187015000 PM
Here is my proc:
procedure sps_get_patientmedrecs_hdr (
p_statusmnemonic_in in varchararrayplstype,
p_processtypemnemonic_in in transactionprocesslog.processtypemnemonic%type,
p_estimatedenddt_in in membermedicalreconcilationhdr.estimatedenddt%type,
p_return_cur_out out sys_refcursor,
p_err_code_out out number,
p_err_mesg_out out varchar2)
is
lv_varchararray varchararray := varchararray();
begin
if p_statusmnemonic_in.count > 0
then
for rec1 in 1..p_statusmnemonic_in.count
loop
lv_varchararray.extend(1);
lv_varchararray(rec1) := p_statusmnemonic_in(rec1);
end loop;
open p_return_cur_out for
select h.membermedreconciliationhdrskey,
h.primarymemberplanid,
h.assigneduserid,
h.accountorgid,
h.reconciliationstatusmnemonic,
h.estimatedenddt,
h.actualenddt,
h.inserteddt,
h.insertedby,
h.updateddt,
h.updatedby
from membermedicalreconcilationhdr h
where h.reconciliationstatusmnemonic in (select *
from table (cast(lv_varchararray as varchararray)))
and h.estimatedenddt <= nvl(p_estimatedenddt_in, h.estimatedenddt)
and not exists (select *
from transactionprocesslog tpl
where tpl.transactiontypemnemonic = 'MEDREC'
and tpl.transactionid = h.primarymemberplanid
and nvl(p_processtypemnemonic_in, tpl.processtypemnemonic) = tpl.processtypemnemonic);
else
open p_return_cur_out for
select h.membermedreconciliationhdrskey,
h.primarymemberplanid,
h.assigneduserid,
h.accountorgid,
h.reconciliationstatusmnemonic,
h.estimatedenddt,
h.actualenddt,
h.inserteddt,
h.insertedby,
h.updateddt,
h.updatedby
from membermedicalreconcilationhdr h;
end if;
p_err_code_out := 0;
exception
when others then
p_err_code_out := -1;
p_err_mesg_out := 'error in ct_cu_act_medrecon_pg.sps_get_patientmedrecs_hdr => ' || sqlerrm;
end sps_get_patientmedrecs_hdr;
I've tried the to_date function but received the same error. Any help would be appreciated, thanks in advance.
Looks like you're treating a string like it's a date, which may or may not work depending on your NLS settings. Whenever Oracle sees a string where it expects a date it tries to do an implicit date conversion based whether that string happens to match your particular session's date formatting parameter. This is a very common source of errors.
To use a proper date you can use the to_date function like so:
to_date('26-JAN-14','DD-MON-RR')
or use the native syntax for specifying date literals, which is what I would recommend:
date'2014-1-26'

How to convert from string to number in Oracle using TO_NUMBER function with fixed decimal point char?

I need to convert in procedure from string to decimal with fixed decimal separator . independant on culture settings. Next, I know decimal number is limited just with 6 decimal places after . and there is no limitiation on number of digits before .. Using Oracle documentation and its examples for format strings I have now just this solution:
v_number := TO_NUMBER(v_string, '9999999999999999999999999999999999D999999', 'NLS_NUMERIC_CHARACTERS = ''. ''');
Number of 9 chars before D is maximum number allowed. I find this format string as pretty awful. Is there any better format string for this general conversion or some way to omit second parameter of function? In general I just need to pass to function NLS parameter to tell it i just want to convert with decimal separator ., but second parameter is mandatory in that case as well.
You can't call the to_number function with the third parameter and not the second. I would suggest putting the "ugly" format string in a package constant and forget about it.
You could also use dbms_session.set_nls to modify your NLS settings and be able to use to_number without arguments.
Handles both comma and period.
FUNCTION to_number2(p_num_str VARCHAR2) RETURN NUMBER AS
BEGIN
RETURN TO_NUMBER(REPLACE(p_num_str, ',', '.'), '999999999999D999999999999', 'NLS_NUMERIC_CHARACTERS=''.,''');
END;
CREATE OR REPLACE FUNCTION IS_NUMBER(P_VAR IN VARCHAR2)
RETURN NUMBER
IS
P_NUMBER NUMBER := 0;
RIG VARCHAR2(10) := '';
FORMAT VARCHAR2(100) := '999999999999D999999999999';
RES VARCHAR2(100) := '';
BEGIN
SELECT VALUE INTO RIG
FROM NLS_DATABASE_PARAMETERS WHERE PARAMETER = 'NLS_NUMERIC_CHARACTERS';
IF SUBSTR(RIG,1,1) = '.' THEN
RES := REPLACE(P_VAR,',','.');
ELSE
RES := REPLACE(P_VAR,'.',',');
END IF;
P_NUMBER := TO_NUMBER(RES,FORMAT,'NLS_NUMERIC_CHARACTERS='''||RIG||'''');
P_NUMBER := ROUND(P_NUMBER,5); --FIVE DIGITS AFTER DECIMAL POINT IS ENOUGH
RETURN P_NUMBER;
EXCEPTION
WHEN OTHERS THEN RETURN -1;
END;
select to_number(' 12.5 ') + to_number(' 12 ') from dual;

Resources