It sounds as if you want a self describing object. Meaning programmatically find the type of a variable without selecting from some metadata view. Just ask the object, what are you?
It seems unnecessary for most situations as in most cases we already know the type (strongly typed). For example, a procedures parameters will typically specify the type (number, varchar2, whatever). Local variables will typically specify the type or tie themselves to a database object type via %type notation.
There are some situations where weakly typed objects are needed or useful, such as a weakly typed cursor variable that can be used for any query. An overly simplistic example:
create or replace procedure get_data(o_cur OUT SYS_REFCURSOR) as
begin
OPEN o_cur FOR
-- without changing parameter, this could select from any table
select * from emp;
end;
Now the problem is that you may have errors (at runtime) if someone codes the cursor to be used with another table (I chose a terrible procedure name on purpose). Something like:
declare
l_cur sys_refcursor;
l_row dept%rowtype;
begin
get_data(l_cur);
-- oops, I thought this was dept data when I coded it, Oracle didn't complain at compile time
LOOP
fetch l_cur
into l_row;
exit when l_cur%notfound;
-- do something here
END LOOP;
close l_cur;
end;
This is also why I prefer strongly typed cursors and avoid this situation.
Anyway, in the case of a self-describing object, you can use SYS.ANYDATA built in type (similarly, SYS.ANYDATASET for generic collection types). This was introduced with 9i I believe. For example, this procedure takes some data and branches logic based on the type:
CREATE OR REPLACE procedure doStuffBasedOnType(i_data in sys.anydata) is
l_type SYS.ANYTYPE;
l_typecode PLS_INTEGER;
begin
-- test type
l_typecode := i_data.GetType (l_type);
CASE l_typecode
when Dbms_Types.Typecode_NUMBER then
-- do something with number
dbms_output.put_line('You gave me a number');
when Dbms_Types.TYPECODE_DATE then
-- do something with date
dbms_output.put_line('You gave me a date');
when Dbms_Types.TYPECODE_VARCHAR2 then
-- do something with varchar2
dbms_output.put_line('You gave me a varchar2');
else
-- didn't code for this type...
dbms_output.put_line('wtf?');
end case;
end;
Here you have your programatic branching based on the type. And to use it:
declare
l_data sys.anydata;
begin
l_data := sys.anydata.convertvarchar2('Heres a string');
doStuffBasedOnType(l_data);
end;
-- output: "You gave me a varchar2"
Hope that wasn't too long winded a response ;)
%type
for all the variables; this springs from that, it's a%rowtype
of a cursor. Either way using%type
doesn't return the data type of that particular type. You are of course right about testing; I'd try to convert to a number and catch the exception but I wondered if there was an in-built way of doing it as Oracle does know the data type so why should I have to test for it again? – Persimmon