I want to bring a string which contains a date to a single format date.
EX:
13-06-2012 to 13-JUN-12
13/06/2012 to 13-JUN-12
13-JUN-2012 to
13-JUN-12
13/jun-2012 to 13-JUN-12
...
I tried to delete all special characters and after that use a function to transform that string into a single format of date. My function return more exceptions, I don't know why...
The function:
CREATE OR REPLACE FUNCTION normalize_date (data_in IN VARCHAR2)
RETURN DATE
IS
tmp_month VARCHAR2 (3);
tmp_day VARCHAR2 (2);
tmp_year VARCHAR2 (4);
TMP_YEAR_NUMBER NUMBER;
result DATE;
BEGIN
tmp_day := SUBSTR (data_in, 1, 2);
tmp_year := SUBSTR (data_in, -4);
--if(REGEXP_LIKE(SUBSTR(data_in,3,2), '[:alpha:]')) then
if(SUBSTR(data_in,3,1) in ('a','j','i','f','m','s','o','n','d','A','J','I','F','M','S','O','N','D')) then
tmp_month := UPPER(SUBSTR (data_in, 3, 3));
else
tmp_month := SUBSTR (data_in, 3, 2);
end if;
DBMS_OUTPUT.put_line (tmp_year);
TMP_YEAR_NUMBER := TO_NUMBER (tmp_year);
IF (tmp_month = 'JAN')
THEN
tmp_month := '01';
END IF;
IF (tmp_month = 'FEB')
THEN
tmp_month := '02';
END IF;
IF (tmp_month = 'MAR')
THEN
tmp_month := '03';
END IF;
IF (tmp_month = 'APR')
THEN
tmp_month := '04';
END IF;
IF (tmp_month = 'MAY')
THEN
tmp_month := '05';
END IF;
IF (tmp_month = 'JUN')
THEN
tmp_month := '06';
END IF;
IF (tmp_month = 'JUL')
THEN
tmp_month := '07';
END IF;
IF (tmp_month = 'AUG')
THEN
tmp_month := '08';
END IF;
IF (tmp_month = 'SEP')
THEN
tmp_month := '09';
END IF;
IF (tmp_month = 'OCT')
THEN
tmp_month := '10';
END IF;
IF (tmp_month = 'NOV')
THEN
tmp_month := '11';
END IF;
IF (tmp_month = 'DEC')
THEN
tmp_month := '12';
END IF;
-- dbms_output.put_line(tmp_day || '~'||tmp_year || '~' ||tmp_month);
IF (LENGTH (tmp_day || tmp_year || tmp_month) <> 8)
THEN
result := TO_DATE ('31122999', 'DDMMYYYY');
RETURN result;
END IF;
-- dbms_output.put_line('before end');
result:=TO_DATE (tmp_day || tmp_month ||tmp_year , 'DDMMYYYY');
-- dbms_output.put_line('date result: '|| result);
RETURN result;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
NULL;
WHEN OTHERS
THEN
result := TO_DATE ('3012299', 'DDMMYYYY');
RETURN result;
RAISE;
END normalize_date;
Usage
SELECT customer_no,
str_data_expirare,
normalize_date (str_data_expirare_trim) AS data_expirare_buletin
FROM (SELECT customer_no,
str_data_expirare,
REGEXP_REPLACE (str_data_expirare, '[^a-zA-Z0-9]+', '')
AS str_data_expirare_trim
FROM (SELECT Q1.set_act_id_1,
Q1.customer_no,
NVL (SUBSTR (set_act_id_1,
INSTR (set_act_id_1,
'+',
1,
5)
+ 1,
LENGTH (set_act_id_1)),
'NULL')
AS str_data_expirare
FROM STAGE_CORE.IFLEX_CUSTOMERS Q1
WHERE Q1.set_act_id_1 IS NOT NULL
)
);
If you have a sound idea of all the possible date formats it might be easier to use brute force:
create or replace function clean_date
( p_date_str in varchar2)
return date
is
l_dt_fmt_nt sys.dbms_debug_vc2coll := sys.dbms_debug_vc2coll
('DD-MON-YYYY', 'DD-MON-YY', 'DD-MM-YYYY', 'MM-DD-YYYY', 'YYYY-MM-DD'
, 'DD/MM/YYYY', 'MM/DD/YYYY', 'YYYY/MM/DD', 'DD/MM/YY', 'MM/DD/YY');
return_value date;
begin
for idx in l_dt_fmt_nt.first()..l_dt_fmt_nt.last()
loop
begin
return_value := to_date(p_date_str, l_dt_fmt_nt(idx));
exit;
exception
when others then null;
end;
end loop;
if return_value is null then
raise no_data_found;
end if;
return return_value;
exception
when no_data_found then
raise_application_error(-20000, p_date_str|| ' is unknown date format');
end clean_date;
/
Be aware that modern versions of Oracle are quite forgiving with date conversion. This function handled dates in formats which aren't in the list, with some interesting consequences:
SQL> select clean_date('20160817') from dual;
CLEAN_DAT
---------
17-AUG-16
SQL> select clean_date('160817') from dual;
CLEAN_DAT
---------
16-AUG-17
SQL>
Which demonstrates the limits of automated data cleansing in the face of lax data integrity rules. The wages of sin is corrupted data.
#AlexPoole raises the matter of using the 'RR' format. This element of the date mask was introduced as a Y2K kludge. It's rather depressing that we're still discussing it almost two decades into the new Millennium.
Anyway, the issue is this. If we cast this string '161225' to a date what century does it have? Well, 'yymmdd' will give 2016-12-15. Fair enough, but what about '991225'? How likely is that the date we really want is 2099-12-15? This is where the 'RR' format comes into play. Basically it defaults the century: numbers 00-49 default to 20, 50-99 default to 19. This window was determined by the Y2K issue: in 2000 it was more likely that '98 referred to the recent past than the near future, and similar logic applied to '02. Hence the halfway point of 1950. Note this is a fixed point not a sliding window. As we move further from the year 2000 the less useful that pivot point becomes. Find out more.
Anyway, the key point is that 'RRRR' does not play nicely with other date formats: to_date('501212', 'rrrrmmdd') hurlsora-01843: not a valid month. So, use'RR'and test for it before using'YYYY'`. So my revised function (with some tidying up) looks like this:
create or replace function clean_date
( p_date_str in varchar2)
return date
is
l_dt_fmt_nt sys.dbms_debug_vc2coll := sys.dbms_debug_vc2coll
('DD-MM-RR', 'MM-DD-RR', 'RR-MM-DD', 'RR-DD-MM'
, 'DD-MM-YYYY', 'MM-DD-YYYY', 'YYYY-MM-DD', 'YYYY-DD-MM');
return_value date;
begin
for idx in l_dt_fmt_nt.first()..l_dt_fmt_nt.last()
loop
begin
return_value := to_date(p_date_str, l_dt_fmt_nt(idx));
exit;
exception
when others then null;
end;
end loop;
if return_value is null then
raise no_data_found;
end if;
return return_value;
exception
when no_data_found then
raise_application_error(-20000, p_date_str|| ' is unknown date format');
end clean_date;
/
The key point remains: there's a limit to how smart we can make this function when it comes to interpreting dates, so make sure you lead with the best fit. If you think most of your date strings fit day-month-year put that first; you will still get some wrong casts but less that if you lead with year-month-day.
The String-to-Date Conversion Rules allow additional formatting rules (without any other modifiers being applied). (Also see this question) So:
MM also matches MON and MONTH;
MON also matches MONTH (and vice versa);
YY also matches YYYY;
RR also matches RRRR; and
The punctuation is optional.
Which means you can do:
CREATE OR REPLACE FUNCTION parse_Date_String(
in_string VARCHAR2
) RETURN DATE DETERMINISTIC
IS
BEGIN
BEGIN
RETURN TO_DATE( in_string, 'DD-MM-YY' );
EXCEPTION
WHEN OTHERS THEN
NULL;
END;
BEGIN
RETURN TO_DATE( in_string, 'MM-DD-YY' );
EXCEPTION
WHEN OTHERS THEN
NULL;
END;
BEGIN
RETURN TO_DATE( in_string, 'YY-MM-DD' );
EXCEPTION
WHEN OTHERS THEN
NULL;
END;
RETURN NULL;
END;
/
Query:
WITH dates ( value ) AS (
SELECT '010101' FROM DUAL UNION ALL
SELECT '02JAN01' FROM DUAL UNION ALL
SELECT '03JANUARY01' FROM DUAL UNION ALL
SELECT '04012001' FROM DUAL UNION ALL
SELECT '05JAN2001' FROM DUAL UNION ALL
SELECT '06JANUARY2001' FROM DUAL UNION ALL
SELECT 'JAN0701' FROM DUAL UNION ALL
SELECT 'JANUARY0801' FROM DUAL UNION ALL
SELECT 'JAN0901' FROM DUAL UNION ALL
SELECT 'JANUARY1001' FROM DUAL UNION ALL
SELECT '990111' FROM DUAL UNION ALL
SELECT '99JAN12' FROM DUAL UNION ALL
SELECT '99JANUARY13' FROM DUAL UNION ALL
SELECT '19990114' FROM DUAL UNION ALL
SELECT '2001-01-15' FROM DUAL UNION ALL
SELECT '2001JAN16' FROM DUAL UNION ALL
SELECT '2001JANUARY17' FROM DUAL UNION ALL
SELECT '20010118' FROM DUAL
)
SELECT value, parse_Date_String( value ) AS dt
FROM dates;
Output:
VALUE DT
------------- -------------------
010101 2001-01-01 00:00:00
02JAN01 2001-01-02 00:00:00
03JANUARY01 2001-01-03 00:00:00
04012001 2001-01-04 00:00:00
05JAN2001 2001-01-05 00:00:00
06JANUARY2001 2001-01-06 00:00:00
JAN0701 2001-01-07 00:00:00
JANUARY0801 2001-01-08 00:00:00
JAN092001 2001-01-09 00:00:00
JANUARY102001 2001-01-10 00:00:00
990111 2099-01-11 00:00:00
99JAN12 2099-01-12 00:00:00
99JANUARY13 2099-01-13 00:00:00
19990114 1999-01-14 00:00:00
2001-01-15 2001-01-15 00:00:00
2001JAN16 2001-01-16 00:00:00
2001JANUARY17 2001-01-17 00:00:00
20010118 0118-01-20 00:00:00
(Note: the date formats you are using are ambiguous, as the last example demonstrates. You can swap the order the formats are parsed in the function to get different results but if you have 010203 is it 01-FEB-2003, 02-JAN-2003, 03-FEB-2001 or even 01-FEB-0003?)
If you want it in the format DD-MON-YY (but why YY and not YYYY?) then just use:
TO_CHAR( parse_Date_String( value ), 'DD-MON-YY' )
Related
Convert a number to Binary's 2's compliment-
I have a sample number in a column of oracle table - 1647795600
I want to convert this to binary 2's compliment.
Expected output-01100010001101110101110110010000
Reference link - https://www.rapidtables.com/convert/number/decimal-to-binary.html
You can create the function:
CREATE FUNCTION dec_to_2c_bin(
value IN PLS_INTEGER,
width IN PLS_INTEGER := 32
) RETURN VARCHAR2 DETERMINISTIC
IS
v_unsigned PLS_INTEGER;
v_binary VARCHAR2(201);
BEGIN
IF value < 0 THEN
v_unsigned := -1 - value;
ELSE
v_unsigned := value;
END IF;
WHILE ( v_unsigned > 0 ) LOOP
v_binary := MOD(v_unsigned, 2) || v_binary;
v_unsigned := TRUNC( v_unsigned / 2 );
END LOOP;
IF LENGTH(v_binary) > width - 1 THEN
RAISE_APPLICATION_ERROR(-20000, 'The value is too large.');
END IF;
v_binary := LPAD(v_binary, width, '0');
IF value < 0 THEN
RETURN TRANSLATE(v_binary, '01', '10');
ELSE
RETURN v_binary;
END IF;
END;
/
Then for the sample data:
CREATE TABLE table_name (value) AS
SELECT +1647795600 FROM DUAL UNION ALL
SELECT -1647795600 FROM DUAL UNION ALL
SELECT 25143 FROM DUAL UNION ALL
SELECT +3142 FROM DUAL UNION ALL
SELECT -3142 FROM DUAL;
The query:
SELECT value, dec_to_2c_bin(value, 64) AS binary2c
FROM table_name;
Outputs:
VALUE
BINARY2C
1647795600
0000000000000000000000000000000001100010001101110101110110010000
-1647795600
1111111111111111111111111111111110011101110010001010001001110000
25143
0000000000000000000000000000000000000000000000000110001000110111
3142
0000000000000000000000000000000000000000000000000000110001000110
-3142
1111111111111111111111111111111111111111111111111111001110111010
db<>fiddle here
I found a statement which worked fine for sample number tested-
select reverse(max(replace(sys_connect_by_path(mod(trunc(&N/power(2,level-1)),2),' '),' ',''))) bin
from dual
connect by level <= 32
;
But might not completely useful, need to modify to read from table.
I want to use my result of function e.g. 'S500,S600,S700,S800' in a subquery in another script like:
where dept_no in (my result of function)
So I want to convert my string result to be like this ('S500','S600','S700','S800').
I tried to do this with dynamic SQL but I can't get it to work.
Hope below snipet suffice your requirement.
Approach 1 -> More effective
--Create a table type of VARCHAR
CREATE OR REPLACE type string_table
IS
TABLE OF VARCHAR2(100);
--Function to return tabl type
CREATE OR REPLACE
FUNCTION string_manipulate
RETURN string_table
AS
str_tab string_table;
BEGIN
SELECT 's00'||level bulk collect INTO str_tab FROM dual CONNECT BY level < 10;
RETURN str_tab;
end;
--Use function in the query
SELECT distinct 1
FROM
(SELECT 's001' dn FROM dual
UNION ALL
SELECT 's002' dn FROM dual
UNION ALL
SELECT 's003' dn FROM dual
UNION ALL
SELECT 's004' dn FROM dual
UNION ALL
SELECT 's005' dn FROM dual
UNION ALL
SELECT 's006' dn FROM dual
UNION ALL
SELECT 's007' dn FROM dual
UNION ALL
SELECT 's008' dn FROM dual
UNION ALL
SELECT 's009' dn FROM dual
)a
WHERE a.dn IN
(SELECT * FROM TABLE(string_manipulate)
);
--Approach 2
--Function to get output as mentioned.
CREATE OR REPLACE
FUNCTION string_manipulate
RETURN VARCHAR2
AS
BEGIN
RETURN 'S2009,S2020,S2021';
END;
-- Use function value in a query
SELECT 1
FROM dual
WHERE '''S2009'',''S2020'',''S2021''' = (''''
||REPLACE(string_manipulate,',',''',''')
||'''');
You need an iterator and text splitting by comma sign:
select empno,ename,sal,deptno
from emp
where empno in (
select to_number(
rtrim(
substr(emps,
instr(emps,',',1,iter.pos)+1,
instr(emps,',',1,iter.pos+1) -
instr(emps,',',1,iter.pos)),',')) emps
from (select ','||'7654,7698,7782,7788'||',' emps from t1) csv,
(select rownum pos from emp) iter
where iter.pos <= ((length(csv.emps) -
length(replace(csv.emps,',')))/length(','))-1
)
But better rewrite your function to return cursor.
you can use collection:
SELECT *
FROM YOUR_TABLE
WHERE DEPT_NO IN (SELECT *
FROM TABLE (SPLIT ('S500,S600,S700,S800')))--splits text with comma, for other chars use split(text, split_char)
With usage of MEMBER OF
SELECT *
FROM YOUR_TABLE
WHERE DEPT_NO MEMBER OF SPLIT ('S500,S600,S700,S800')--splits text with comma, for other chars use split(text, split_char)
the split fuction is:
CREATE OR REPLACE TYPE SPLIT_TBL AS TABLE OF VARCHAR2 (32767);
CREATE OR REPLACE FUNCTION SPLIT (P_LIST VARCHAR2, P_DEL VARCHAR2 := ',')
RETURN SPLIT_TBL
PIPELINED
IS
L_IDX PLS_INTEGER;
L_LIST VARCHAR2 (32767) := P_LIST;
BEGIN
LOOP
L_IDX := INSTR (L_LIST, P_DEL);
IF L_IDX > 0
THEN
PIPE ROW (SUBSTR (L_LIST, 1, L_IDX - 1));
L_LIST := SUBSTR (L_LIST, L_IDX + LENGTH (P_DEL));
ELSE
PIPE ROW (L_LIST);
EXIT;
END IF;
END LOOP;
RETURN;
END SPLIT;
FUNCTION GET_TS_EACH_DAY_DEPARTMENT (P_SER_NO VARCHAR2,
P_TS_DATE DATE
)
RETURN STRING_TABLE
IS
V_DEPT_NO VARCHAR2 (4000);
V_DEPT VARCHAR2(4000);
V_TABLE STRING_TABLE:=STRING_TABLE();
J NUMBER:=1;
BEGIN
for i in (select distinct ts_day dayy from WEB_TS_USER_LOCATIONS_V ) loop
V_TABLE.EXTEND;
V_TABLE(J):= WEB_TS_PKG.GET_TS_DAY_DEPARTMENT (P_SER_NO ,P_TS_DATE , i.dayy );
J:=J+1;
end loop;
RETURN V_TABLE;
END GET_TS_EACH_DAY_DEPARTMENT;
I'm writing a package to learn Oracle. I want to create an OVERLAP function that checks if two date ranges overlap each other.
FUNCTION OVERLAP(p_START_DATE_1 DATE, p_END_DATE_1 DATE,
p_START_DATE_2 DATE, p_END_DATE_2 DATE) RETURN VARCHAR2 AS
lv_RESULT VARCHAR2(1);
BEGIN
lv_RESULT := SELECT 'T' AS overlap FROM dual
WHERE (p_START_DATE_1, p_END_DATE_1) overlaps (p_START_DATE_2, p_END_DATE_2);
IF (lv_RESULT = 'T')
RETURN 'T';
RETURN 'N';
END OVERLAP;
I tried to execute my function, but getting an error ORA-04063: package body 'XYZ' contains errors...
SELECT KP_XYZ_PACKAGE_SQL.OVERLAP(
TO_DATE('01/01/2014', 'DD/MM/YYYY'),
TO_DATE('01/12/2014', 'DD/MM/YYYY'),
TO_DATE('01/02/2014', 'DD/MM/YYYY'),
TO_DATE('01/05/2014', 'DD/MM/YYYY'))
FROM DUAL;
I think SELECT works fine. But the error occurs (I suppose) here: lv_RESULT := SELECT.... Why?
Try this:
CREATE OR REPLACE FUNCTION OVERLAP(p_START_DATE_1 DATE, p_END_DATE_1 DATE,
p_START_DATE_2 DATE, p_END_DATE_2 DATE) RETURN VARCHAR2 AS
lv_RESULT VARCHAR2(1);
BEGIN
lv_RESULT := 'N';
SELECT 'T' into lv_RESULT FROM dual
WHERE (p_START_DATE_1, p_END_DATE_1) overlaps (p_START_DATE_2, p_END_DATE_2);
IF (lv_RESULT = 'T') THEN
RETURN 'T';
END IF;
RETURN 'N';
END OVERLAP;
The IF statement was incomplete too - THEN and END IF were missing, which I have now added.
The documentation for SELECT INTO statement is here. There are links to examples at the end of the page.
Corrected version : return 'T' for True 'N' for False (?)
CREATE OR REPLACE FUNCTION OVERLAP(p_START_DATE_1 DATE, p_END_DATE_1 DATE,
p_START_DATE_2 DATE, p_END_DATE_2 DATE) RETURN VARCHAR2 AS
lv_RESULT VARCHAR2(1);
BEGIN
SELECT 'T' into lv_RESULT FROM dual WHERE (p_START_DATE_1, p_END_DATE_1) overlaps (p_START_DATE_2, p_END_DATE_2);
RETURN lv_RESULT;
EXCEPTION
WHEN NO_DATA_FOUND THEN lv_RESULT := 'N';
RETURN lv_RESULT;
END OVERLAP;
/
I get an error when the input for iv_from_date is given in this format 04/01/2013,it works perfectly when the input is in this format 04/Jan/2013.the nls date format is DD-MON_RR.My input should be in this format 04/01/2013.How do i do this
CREATE OR REPLACE procedure Sp_SaveNewTaxPercentage(iv_category_id number,
iv_tax_percentage number,
iv_from_date varchar2,
iv_to_date varchar2 default '12/31/9998',
iv_created_date date,
ov_err_code out nocopy varchar2,
ov_err_msg out nocopy varchar2) is
lv_category_id varchar2(25);
LV_TO_DATE varchar2(25);
lv_cat_id varchar2(12);
IV_TAX_ID VARCHAR2(12);
lv_tax_percentage varchar(20);
begin
ov_err_code := 0;
SELECT MAX(TAX_ID)
INTO IV_TAX_ID
FROM TAX_P
WHERE CATEGORY_ID IN (iv_category_id);
for J in (SELECT CATEGORY_ID, FROM_DATE, tax_percentage
FROM TAX_P
WHERE TAX_ID = IV_TAX_ID) loop
SELECT to_date(iv_from_date, 'dd/mm/yyyy') - 1
INTO LV_TO_DATE
FROM DUAL;
lv_cat_id := J.CATEGORY_ID;
lv_tax_percentage := j.tax_percentage;
begin
UPDATE TAX_P
SET TO_dATE = LV_TO_DATE
WHERE TAX_PERCENTAGE = lv_tax_percentage
and to_date = iv_to_date;
commit;
insert into tax_p
(TAX_ID,
CATEGORY_ID,
TAX_PERCENTAGE,
FROM_DATE,
TO_DATE,
CREATE_DATE)
values
(tax_seq.nextval,
iv_category_id,
to_char(iv_tax_percentage, '99D99'),
iv_from_date,
iv_to_Date,
sysdate);
commit;
end;
end loop;
IF LV_CAT_ID IS NULL THEN
insert into tax_p
(TAX_ID,
CATEGORY_ID,
TAX_PERCENTAGE,
FROM_DATE,
TO_DATE,
CREATE_DATE)
values
(tax_seq.nextval,
iv_category_id,
to_char(iv_tax_percentage, '99D99'),
iv_from_date,
IV_TO_DATE,
sysdate);
END IF;
commit;
select 'Successfully Saved' into ov_err_msg from dual;
commit;
Exception
when others then
rollback;
ov_err_code := 1;
ov_err_msg := 'Error while saving' || SQLERRM;
end Sp_SaveNewTaxPercentage;
Please avoid using VARCHAR2 type for variables and parameters thar are actually of type DATE. Oracle tries to convert your varchar parameter to date implicitly. When format is the same as set in NLS_DATE_FORMAT it succeeds else it fails. Make both iv_from_date and iv_to_date typed as DATE. If respective columns in table TAX_P are typed as varchar2 then make explicit format conversion in insert statement.
I have a table with this values:
ID VALUE
-----------------------
23559 200
23562 -1 & {14376}#-1
and I want to do to a select that if I cannot convert to number set NULL.
I generally use translate for this because it is such an odd corner case:
SELECT
CASE
WHEN NOT TRIM(TRANSLATE(COLUMN_NAME, '1234567890', ' ')) IS NULL THEN NULL
ELSE COLUMN_NAME
END AS "NUMERIC_COLUMN"
FROM
TABLE_NAME;
If necessary, that can be turned into a procedure, but I'm not sure that there would be terribly much benefit performance-wise.
You can create a function that tries to convert the string to a number and catches the exception. Something like
CREATE OR REPLACE FUNCTION my_to_number( p_str IN VARCHAR2 )
RETURN NUMBER
IS
l_num NUMBER;
BEGIN
BEGIN
l_num := to_number( p_str );
EXCEPTION
WHEN others THEN
l_num := null;
END;
RETURN l_num;
END;
Then you can
SELECT id, my_to_number( value )
FROM your_table
You could also use REGEXP_LIKE:
SELECT id
, CASE WHEN regexp_like(value,'^[0-9]+$') THEN TO_NUMBER(value)
ELSE NULL
END value
FROM your_table;
For example:
SQL> WITH q AS (
2 SELECT 1 ID, '200' col FROM dual
3 UNION
4 SELECT 2, '-1 & {14376}#-1' FROM dual
5 )
6 SELECT id, CASE WHEN regexp_like(col,'^[0-9]+$') THEN TO_NUMBER(col) ELSE NULL END TEST FROM q;
ID TEST
---------- ----------
1 200
2
With Oracle 12.2 this can be done a bit easier using the on conversion error option:
select id, cast(value as number default null on conversion error) as value
from the_table;
Optionally you can also specify a format mask, similar to the to_number() function.
I assume this would be faster than using a PL/SQL function, not sure about the performance compared to a case with a regex. But it is definitely a lot shorter.
CREATE OR REPLACE FUNCTION asnumber(p_val IN VARCHAR2) RETURN NUMBER IS
l_val NUMBER;
BEGIN
l_val := TO_NUMBER(p_val);
RETURN l_val;
EXCEPTION WHEN VALUE_ERROR THEN
RETURN null;
END;