How can I easily use a collection in an Oracle where clause in a package? - oracle

It is my understanding that you cannot use a collection in a where clause unless it is defined at the DB level. I have a distinct dislike for random type definitions laying about a schema. It's a religious thing so don't try to dissuade me.
Types contained within a package are cool, because they are easily found and are related to the work at hand. So having said that I have a package that defines a structure (currently a table type collection) that looks like;
TYPE WORD_LIST_ROW IS RECORD(
WORD VARCHAR(255));
TYPE WORD_LIST IS TABLE OF WORD_LIST_ROW;
There is a routine in the package that instantiates and populates an instance of this. It would be useful to be able to use the instantiated object, or some analog therof in a where clause.
So being the clever (or so I thought) programmer, I said why don't I just create a pipelined function to make a table from the collection which I did, and it looks like;
FUNCTION WORD_LIST_TABLE(IN_WORD_LIST WORD_LIST) RETURN WORD_LIST PIPELINED
AS
OUT_WORD_LIST WORD_LIST := WORD_LIST();
BEGIN
FOR I IN 1 .. IN_WORD_LIST.COUNT
LOOP
PIPE ROW(IN_WORD_LIST(I));
END LOOP;
RETURN;
END WORD_LIST_TABLE;
Then in another routine I call the function that builds the collection, finally I use a pipelined function that uses the collection as input in a cursor's where clause.
sort of like this;
cursor xyz
is
select * from x-stuff where fieldA in (select word from table(word_list_table(temp_word_list));
In the loop for the cursor I get an oracle error ora-21700 object does not exist or is marked for delete.
Is there any easy way to build an oracle object that can be used in an Oracle where clause? Basically what I would like to do is;
select * from whatever where fielda in myobject;

The solution is simple - declare the type at schema level using CREATE TYPE statement and you will be able to use your collections in your SQL statements in PL/SQL blocks.
If you have declared your TYPE inside a PL/SQL package you cannot use it in your queries inside PL/SQL blocks.
Also, you must keep in mind that only varray and nested table type collections can be used in queries as of Oracle 11.2 and you cannot use associative arrays in queries.. In 12c you don't have these restrictions.
For further reference go to Oracle Docs.

Related

How to use Variable in Alias Oracle PL/SQL

I try to use variable in the alias.
Is it possible not to change to dynamic SQL???
For example
get_this_year --> this is the function call this year, so 2018.
CREATE OR REPLACE PROCEDURE do_activity
v_cur_year VARCHAR2(11);
BEGIN
v_cur_year := get_this_year;
select t.1 AS v_cur_year -- I wanna use in here
FROM table1
END do_activity
how can I use Variable as alias.
Thanks
What you are asking for is not possible.
The column list, names, structure, etc. has to be known, when the query is parsed. Queries within PL/SQL are parsed, when PL/SQL code is parsed/compiled, so in your case, on procedure creation. (This obviously excludes dynamic queries, which are constructed in run-time, hence can't be parsed on PL/SQL compilation.)
You would have to use the dynamic SQL, to get the column name defined by the function result, but you already stated, that you do not want dynamic SQL.
There's a second issue with your code, although it may be a result of you simplifying the code. You are not capturing the query result in your procedure, which is obligatory in Oracle PL/SQL. You can't just run a query, and expect its result to be returned by running the procedure - it's not Transact-SQL. To return a data set from PL/SQL, you would have to write a tabular function (still, this would require stable data set structure, so no dynamic column naming) or you would have to use an OUT parameter of ref-cursor type.

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.

PL/SQL Creating Associative Array With Employee Id as Key

I am trying to create a hash with employee_id (NUMBER(6,0)) as the key and salary (NUMBER(8,2)) as the value.
For that I have created a INDEX-OF table(associative array) in PL/SQL (Oracle 11g) using the following definition:
TYPE emp_title_hash IS TABLE OF employees.salary%type
INDEX BY employees.employee_id%type;
I am getting the following compilation error:
Error(22,28): PLS-00315: Implementation restriction: unsupported table index type
I am aware in this case that the only supported type for the index is STRING or PLS_INTEGER. This seems to really restrictive. Why exactly has this been imposed in Oracle ? Is there a work around to get the above done ?
Appreciate your comments / suggestions.
As someone already pointed out, you can use "index by pls_integer", since pls_integer can contain any number(6,0) value.
Surely it would be nice to be able to use any possible type to index a pl/sql associative array, but I have always managed a way of writing a function that builds a string that identifies the object instance I want to use as index value.
So, instead of writing:
type TMyAssociativeArray is table of MyDataType index by MyIndexType;
I write:
Type TMyAssociativeArray is table of MyDataType index by varchar2(30);
then I write a function that calculates a unique string from MyIndexType:
function GetHash(obj MyIndexType) return varchar2;
and, having written this function I can use it to simulate an associative array indexed by MyIndexType object:
so I can write
declare
arr TMyAssociativeArray;
obj TMyDataType;
idx TMyDataType;
begin
....
arr(GetHash(idx)) := obj;
end;
Here I am getting out of the strict question you asked, and giving you an advice about a possible other way of obtaining a quick customer->salary lookup cache... This is the final purpose of your associative-array question, as I can read from your comment, so maybe this could be useful:
If you are using associative arrays to build a fast look-up mechanism, and if you can use the oracle 11g R2 new features, a easier way of obtaining this caching is to rely on the native RESULT_CACHE feature which has been introduced both for queries (by using the RESULT_CACHE hint) and for pl/sql functions.
for pl/sql functions you can create a function whose result value is cached like this one:
create or replace function cached_employee_salary(employee number)
return number RESULT_CACHE is
result number;
begin
select salary
into result
from employees e
where e.code = employee;
return result;
end;
the RESULT_CACHE keyword instructs oracle to keep an in-memory cache of the result values and to reuse them on subsequent calls (when the function is called with the same parameters, of course).
this cache has these advantages, compared to the use of associative arrays:
it is shared among all sessions: cached data is not kept in the private memory allocated for each session, so it wastes less memory
oracle is smart enough to detect that the function calculates its results by accessing the employees table and automatically invalidates the cached results if the table data is being modified.
Of course I suggest you to run some tests to see if this optimization, in your case gives some tangible results. it mostly depends on how complex is the calculation you make in your function.
You can also rely on an analogous feature introduced for SQL queries, triggered by the /+RESULT_CACHE/ hint:
select /*+RESULT_CACHE*/
salary
from employees e
where e.code = employee;
return result
this hint instructs oracle to store and reuse (on subsequent executions) the result set of the query. it will be stored too in memory.
actually this hint has also the advantage that, being the hint -syntactically speaking- a special comment, this query will continue working without any modifications even on servers < 11gR2, whereas, for the function cache version, you should use some "conditional compilation" magic to make it compile also with previous server versions (for which it would be a normal function without any result caching)
I Hope this helps.

ANYDATA with Collections based on rowtype

I just discovered Oracle's ANYDATA and ANYTYPE and thought it would be useful to generic-ify some very repetitive code I have. I tried a very simple code sample that looks like this:
declare
--simple test query
cursor myCurs is
select sysdate from dual;
type t_arr is table of myCurs%rowtype;
v_arr t_arr;
v_anydata ANYDATA;
begin
open myCurs;
fetch myCurs bulk collect into v_arr;
close myCurs;
dbms_output.put_line(v_arr.count || ' records');
v_anydata := ANYDATA.convertCollection(v_arr);
dbms_output.put_line(anydata.gettypename(v_anydata));
end;
/
Running this code gives me the error "ORA-22370: incorrect usage of method ORA-22370 incorrect ussage of method ConvertCollection" (Yes, it actually repeats ORA-22370).
My question is: Is it possible to use ANYDATA on collections of rowtypes? I ask this because all examples I've seen of ANYDATA (so far) with collections use object types, none use rowtypes, but I haven't seen anything (yet) that explicitly says that rowtypes can't be used.
My goal was to clean up some very repetitive bulk-collect/insert code that does the same thing over and over, but with different cursors for different tables.
(Oracle 10g)
ANYDATA works with SQL data_types, i.e. anything you could use to define a table column.
%ROWTYPE is a PL/SQL construct, and so is not recognised by the SQL engine. If you think about the dynamic nature of %ROWTYPE you'll be able to figure out why.
Wanting to clean up repetitive code is a noble idea. I have previously implemented an ETL processor in SQL Types which used a supertype Object to own the generic processing, and subtypes to execute table specific code (such as inserts). These subtypes are completely generatable: not as slick as a dynamic %ROWTYPE implementation would have been, but still easy enough.

What is the difference between "AS" and "IS" in an Oracle stored procedure?

I see Oracle procedures sometimes written with "AS", and sometimes with "IS" keyword.
CREATE OR REPLACE Procedure TESTUSER.KILLINSTANCE (INSTANCEID integer) **AS**
...
vs.
CREATE OR REPLACE Procedure TESTUSER.KILLINSTANCE (INSTANCEID integer) **IS**
...
Is there any difference between the two?
Edit: Apparently, there is no functional difference between the two, but some people follow a convention to use "AS" when the SP is part of a package and "IS" when it is not. Or the other way 'round. Meh.
None whatsover. They are synonyms supplied to make your code more readable:
FUNCTION f IS ...
CREATE VIEW v AS SELECT ...
One minor difference...
They are synonyms for packages and procedures, but not for cursors:
This works...
cursor test_cursor
is
select * from emp;
... but this doesn't:
cursor test_cursor
as
select * from emp;
"IS" and "AS" act as a synonym while creating procedures and packages but not for a cursor, table or view.
Here's another difference (in 10g, at any rate)
Given a loose object type:
CREATE TYPE someRecordType AS OBJECT
(
SomeCol VARCHAR2(12 BYTE)
);
You can create a loose Table type of this object type with either AS or IS
CREATE OR REPLACE TYPE someTableType
IS {or AS} TABLE OF someRecordType;
However, if you create this same table type within a package, you must use IS:
CREATE OR REPLACE PACKAGE SomePackage IS
TYPE packageTableType IS TABLE OF someRecordType;
END SomePackage;
Use of AS in the package yields the following error:
Error(2,30): PLS-00103: Encountered the symbol "TABLE" when expecting one of the following: object opaque
According to TutorialsPoint
The AS keyword is used instead of the IS keyword for creating a
standalone procedure.
and considering previous answers,
I guess
AS is for stand alone (outside of any block, subprogram, package) entities
and
IS is for embedded (within a block, subprogram or package) entities.
.
The AS keyword is used instead of the IS keyword for creating a standalone function.
[ A standalone stored function is a function (a subprogram that returns a single value) that is stored in the database.
Note: A standalone stored function that you create with the CREATE FUNCTION statement is different from a function that you declare and define in a PL/SQL block or package. ]
For more explanation, read this...

Resources