PL/SQL Package Table - oracle

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

Related

User defined table types in Oracle

First of all usually I am working with MSSQL. But I have a stored procedure in MSSQL, which I need to use in Oracle now and since I am absolutely new to Oracle I have no idea at all how to do it correct.
I needed to use user defined table types in my MS SQL stored procedure because I am using "logical" tables in my stored procedure, which I also need to pass them to a dynamic sql statement within this procedure (using column names of "physical" tables as variables/parameters).
I've started to add the oracle function in a package I made before for another function. It looks like
TYPE resultRec IS RECORD
(
[result columns]
);
TYPE resultTable IS TABLE OF resultRec;
Function MyFunctionName([A LOT PARAMETERS]) RETURN resultTable PIPELINED;
I also described the layout of the tables (the user defined table types in MSSQL), which I want to use within this function in this package header.
So far so good, but now I don't really know where I have to declare my table variables or user defined table types. I also tried to put them in the package header, but if I am trying to use these tables in the package body, where I am describing my function, Oracle tells met, that the table or view does not exist.
I also tried it to describe the tables within the package body or in the block of my function, which looks like that:
FUNCTION MyFunctionName
(
[MyParameters]
)
RETURN resultTable PIPELINED is rec resultrec;
TYPE tableVariableA IS TABLE OF tableRecA;
TYPE tableVariableB IS TABLE OF tableRecB;
BEGIN
INSERT INTO tableVariableA
SELECT ColumnA, ColumnB FROM physicalTable WHERE[...];
[A LOT MORE TO DO...]
END;
But in this case Oracle also tells me, that it doesn't know the table or view.
I also tried a few more things, but at the end I wasn't able to tell Oracle what table it should use...
I would appreciate every hint, which helps me to understand how oracle works in this case. Thanks a lot!
You can't insert into a collection (e.g. PL/SQL table). You can use the bulk collect syntax to populate the collection:
SELECT ColumnA, ColumnB
BULK COLLECT INTO tableVariableA
FROM physicalTable
WHERE [...];
However, you might want to check this is an appropriate approach, since SQL Server and Oracle differ quite a bit. You can't use PL/SQL tables in plain SQL (at least prior to 12c), even inside your procedure, so you might need a schema-level type rather than a PL/SQL type, but it depends what you will do next. You might not really want a collection at all. Trying to convert T-SQL straight to PL/SQL without understanding the differences could lead you down a wrong path - make sure you understand the actual requirement and then find the best Oracle mechanism for that.

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

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.

Oracle: Using Procedure or Cursor to store a Select-Statement?

I have a PL/SQL package where i want to declare a select-statment which are used by different other Packages. So i see to ways. First way i define a cursor which can be called from other packages and store the select. Second way would be a procedure which stored the select.
Can someone tell me the advantages and disadvantages of each way? My Prof. say Cursor are old and statefull and noone use this today. My Chef tell me Cursor is faster to iterate and you can make Types of it.
Can someone tell me what's the best practice here?
For example:
CURSOR crs_active_customer IS
SELECT * FROM customer where status = 'active'
OR
PROCEDURE prc_getActiveCustomer IS
BEGIN
SELECT * FROM customer where status = 'active';
END prc_getActiveCustomer;
What is better way to store select-statements.
I would write a function that returns a new cursor instance every time you call it. A cursor variable in a package is actually a GLOBAL variable: you can have only one procedure at a time using it. This is probably the problem your professor is referring to.
Having a global cursor means that you will run into "cursor already open" errors if you write a a procedure that, while scanning the results of such cursor calls another function that internally needs to use the same cursor.
PL/SQL 101 to the rescue ! From Working with Cursors:
The central purpose of the Oracle PL/SQL language is to make it as easy and efficient as possible to query and change the contents of tables in a database. You must, of course, use the SQL language to access tables, and each time you do so, you use a cursor to get the job done.
So every time you have SQL in PL/SQL there will be a cursor. The next question is what kinds of cursors there is and when to use them. The above mentioned article touches also this topic.
You can also read the fine manual: Cursors
A cursor is a pointer to a private SQL area that stores information about processing a specific SELECT or DML statement.
And then carry on reading about implicit and explicit cursors.
Next find a better professor.

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.

Call data-specific stored procedure from Oracle procedure

I have a situation where I have an Oracle procedure that is being called from at least 3 or 4 different places. I need to be able to be able to call custom-code depending on some data. The custom-code is customer-specific - so, customer A might want to do A-B-C where customer B might want to do 6-7-8 and customer C doesn't need to do anything extra. When customers D...Z come along, I don't want to have to modify my existing procedure.
I'd like to be able to enter the customer-specific procedure into a table. In this existing procedure, check that database table if a custom-code procedure exists and if so, execute it. Each of the customer-code procedures would have the same parameters.
For instance:
My application (3+ places) calls this "delete" procedure
In this delete procedure, look up the name of a child-procedure to call (if one exists at all)
If one exists, execute that delete procedure (passing the parameters in)
I know I can do this with building a string that contains the call to the stored procedure. But, I'd like to know if Oracle 10g has anything built in for doing this kind of thing?
Do each of your customers have their own database? If so the best option would be to use conditional compilation. This has the advantage of not requiring dynamic SQL. Have the main program always call the custom procedure, and use CC flags to vary the code it contains.
Otherwise, Oracle does have a Rule Engine but it is not really intended for our use.
The final solution that we went with was to store the name of a procedure in a database table. We then build the SQL call and use an EXECUTE statement.
Agree with APC's answer and just to expand on it, in this white paper if you look for "Component based installation" it describes a similar problem solved by using conditional compilation.
Your solution seems reasonable given the requirements, so I voted it up.
Another option would be to loop through the results from your table look-up and put calls to the procedures inside a big case statement. It would be more code, but it would have the advantage of making the dependency chain visible so you could more easily catch missing permissions and invalid procedures.

Resources