oracle declare a user defined type that can be used across multiple functions and procedure - oracle

I have an SQL file that creates other stored procedures/functions. I wanted to make use of a user defined type that can be accessed among the created stored procedures/functions. What is the best way to achieve this? The simplest way that I thought of is to just create that user defined type in every procedure/function.
My user defined type looks something like this:
create type array_t is varray(2) of number;

Just execute
create type array_t is varray(2) of number;
globally.

You can either create the type as a stand-alone item, with a create [or replace] type statement, or else include it in a package header. The advantage of a standalone type is that you can use it in SQL queries and as the type for table and view columns. The advantage of a PL/SQL-only declaration is that it simplifies your deployment, and nobody in their right mind would want a varray column anyway.
If your deployment process is giving 'name is already used by an existing object' errors then yes it already exists (perhaps from a previous run). create or replace is standard practice and a good idea, although your scripts should define each item in only one place.

Related

Use global type instead of the local subtype with same name

I have a oracle database with several packages and some types. There is a type defined like
create or replace type my_type as table of varchar2(4000)
And then, into pkg_machines there is
subtype my_type is varchar2(4000)
Now, I didn't write the database and while analyzing errors I discovered that several packages do use pkg_machines.my_type, but pkg_machines needs to use both the local and the global one.
The only possible way that came in my mind to do this, is to change the name of the subtype and refactorize every other package that uses it, but it would mean to waste a lot of time on refactoring hundreds of packages.
My question is: is there a way to tell to a procedure to refer to the global type my_type instead of the defined subtype?
When you want to use the global name fully qualify it as schema_name.my_type. The schema name will usually be the same as the user you were logged in to the database with when the type was created.
Share and enjoy.

Simple type alias - Best practice for Oracle

I have a multi-schema oracle DB in which client account should be varchar(50) in all the schemas. Therefore, I'd like to assign a new name to varchar(50) like MYCLIENT such that in all table, sp and functions, I simply use MYCLIENT to define a field, parameter, etc. to avoid misintegrity.
1 - How should I define the new type in Oracle (The simplest method)
2 - Where to define it (Schema, package, DB, ..) in accordance to the best practices?
Thanks a lot
I'm afraid there's no simple way to achieve this. You could define a subtype inside a package like this:
SUBTYPE myclient is VARCHAR2(50);
but you would not be able to use this as a type for database column. In order to do so, you would have to define a SQL type with:
CREATE TYPE myclient ...
but you are only able to define record types, object types or collection types this way. This is weird, but well, just as quite a few things in Oracle... :)

Passing an associative array as a parameter between packages

I've got two separate Oracle (v9.2) PL/SQL packages and I'm trying to pass an associative array (ie, index-by table) from a procedure in package1, as a parameter to a procedure in package2. Is this possible? I keep getting PLS-00306: wrong number or types of arguments in call to 'ROLLUP_TO_15' when I compile package1.
The array is defined as:
type list_tab is table of number(10)
index by binary_integer;
in both package's spec. In the procedure in package1, I'm calling the second package as package2.rollup_to_15(chanList); That's the line I get the compile error on (chanList is a variable of type list_tab).
In package2, the procedure is defined as:
procedure rollup_to_15(channels in list_tab) is
I'm guessing that my problem is that the type is defined separately in each package, because I can pass the `chanList' variable to other procedures within the first package without any problems.
So, is it possible to pass an associative array between packages? And if so, how?
Dave
Yes, it's possible for sure.
It's hard to explain why do you receive error without package specs samples, but in general to pass a user-defined type as a parameter you should either with define type DDL, or defining the type in package spec.
I suppose you want the latter variant :)
So here're an example:
create or replace package TestPackage_1
as
type TTestType is table of varchar2(1) index by varchar2(1);
end TestPackage_1;
/
create or replace package TestPackage_2
as
procedure Dummy(aParam TestPackage_1.TTestType);
end TestPackage_2;
/
You can use TTestType type in any PL/SQL block, but not in SQL.
"The array is defined as: ... in both
package's spec."
This is the source of your problem. PL/SQL regards two separate declarations as two different objects, even though both types have an identical signature. Consequently the engine hurls when you call this:
package2.rollup_to_15(chanList)
Your code has defined the chanList variable as package1.list_tab but the procedure is expecting a variable of type package2.list_tab.
The simplest solution is to declare LIST_TAB just in PACKAGE2, and chnage PACKAGE1 so that chanList is declared appropriately.

PL/SQL Package Table

I need to maintain state in a PL/SQL application. It needs to hold a small table during the session.
As I understand it, this is accomplished via a package variable, but I don't know how to create a table as a package variable.
Anyone explain how to do this or alternatives?
Expansion of Problem:
I have a WHERE IN condition that I must populate in a cursor at run time. Since to my knowledge I can only populate it with a hard-coded literal or a SELECT I need to hold all the IN's that are selected by the user during the session.
You define a variable with a table type in the package. If you want the state accessible from outside the package it is defined in the header - if you want it private then you define it in the body.
If you want to initialise the variable the first time the package is accessed then you use an initialisation block at the bottom of the package.
Some tips:
Be careful with exception handling when using initialisation blocks. If an exception is raised you need to ensure you use clear error messages or log messages. A maintenance programmer troubleshooting an issue may jump straight to the called method to troubleshoot rather than examining the implicit initialisation block.
Oracle can shuffle packages in and out of memory under various conditions at which point the package level variable is cleared. Ensure your state is required just for that session and that the session is fairly short lived (i.e not around for days). If you need more reliable persistence then use a physical table, not a package variable.
I often find a problem that starts out as being suitable for a package-level table usually grows more complex over time. It might be better to use a real table from an extensibility point of view. It depends on whether you are looking for a short-term solution for a simple problem or a long-term solution for a mission-ritical problem or a problem that will evolve over time.
Example using a simple "name-value" mapping table:
create or replace package bob as
procedure do_stuff;
end bob;
create or replace package body bob as
type my_table is table of varchar2(100) index by varchar2(100);
my_variable my_table;
procedure do_stuff
begin
--do stuff to my_variable
end;
begin
--initialise my_variable
end bob;
If you need to maintain state in your application, you can do this in a global table just as easily as a package-level table. The difference would be that the package table will not be accessible outside of the package.
From O'Reilly, we see that a table defined in the PACKAGE specification is considered global, but a table declared in the PACKAGE BODY specification is a package-level var.
A GLOBAL TEMP TABLE set to on Commit Preserve Rows that is setup as Key/Value can help in using the values throughtout the session not just within the package. SQL queries can then be built around IN or EXIST clauses with the table using access by the key.
Here is the Oracle Doc on Global Temp Tables

Referencing Oracle user defined types over DBLINK?

I'm working in two different Oracle schemas on two different instances of Oracle. I've defined several types and type collections to transfer data between these schemas. The problem I'm running into is that even though the type have exactly the same definitions (same scripts used to create both sets in the schemas) Oracle sees them as different objects that are not interchangeable.
I thought about casting the incoming remote type object as the same local type but I get an error about referencing types across dblinks.
Essentially, I'm doing the following:
DECLARE
MyType LocalType; -- note, same definition as the RemoteType (same script)
BEGIN
REMOTE_SCHEMA.PACKAGE.PROCEDURE#DBLINK( MyType ); -- MyType is an OUT param
LOCAL_SCHEMA.PACKAGE.PROCEDURE( MyType ); -- IN param
END;
That fails because the REMOTE procedure call can't understand the MyType since it treats LocalType and RemoteType as different object types.
I tried DECLARING MyType as follows as well:
MyType REMOTE_SCHEMA.RemoteType#DBLINK;
but I get another error about referencing types across dblinks. CASTing between types doesn't work either because in order to cast, I need to reference the remote type across the dblink - same issue, same error. I've also tried using SYS.ANYDATA as the object that crosses between the two instance but it gets a similar error.
Any ideas?
UPDATE:
Tried declaring the object type on both sides of the DBLINK using the same OID (retrieved manually using SYS_OP_GUID()) but Oracle still "sees" the two objects as different and throws a "wrong number or types of arguements" error.
I have read the Oracle Documentation and it is not very difficult.
You need to add an OID to your type definitions in both databases.
You can use a GUID as OID.
SELECT SYS_OP_GUID() FROM DUAL;
SYS_OP_GUID()
--------------------------------
AE34B912631948F0B274D778A29F6C8C
Now create your UDT in both databases with the SAME OID.
create type testlinktype oid 'AE34B912631948F0B274D778A29F6C8C' as object
( v1 varchar2(10) , v2 varchar2(20) );
/
Now create a table:
create table testlink
( name testlinktype);
insert into testlink values (testlinktype ('RC','AB'));
commit;
Now you can select from the table via the dblink in the other database:
select * from testlink#to_ora10;
NAME(V1, V2)
--------------------------
TESTLINKTYPE('RC', 'AB')
If you get error ORA-21700 when you try to select via the dblink the first time, just reconnect.
I think the underlying issue is that Oracle doesn't know how to automatically serialize/deserialize your custom type over the wire, so to speak.
Your best bet is probably to pass an XML (or other) representation over the link.

Resources