CREATE OR REPLACE PACKAGE pkg_nvl
AS
FUNCTION defaultNVL (tab IN TABLE)
RETURN TABLE
PIPELINED ROW POLYMORPHIC USING pkg_nvl;
FUNCTION describe (tab IN OUT dbms_tf.table_t)
RETURN dbms_tf.describe_t;
PROCEDURE fetch_rows;
END pkg_nvl;
I've defined the function defaultNVL is pkg_NVL. It's a polymorphic table function. But when I search the function in dba_procedures althougth I find it, it's not marked as polymorphic.
select polymorphic from dba_procedures where and object_name like '%NVL%';
NULL
why?
code (In dbfiddle, I've changed dba_procedures by user_procedures because this table doesn't exist)
Related
create package pa as
type ra1 is record (
one integer,
two integer
);
type ra2 is record (
r1 ra1,
three integer,
fore integer
);
type ta1 is table of ra1;
type ta2 is table of ra2;
function pa1 return ta1 pipelined;
function pa2 return ta2 pipelined; --pipelined functions must have a supported collection return type
end;
It doesn't seem possible too create a table of a record.
I explain why I want to do that. Perhaps you a another solution.
I have a big query with a "with statement" with n part.
Some parts are reused in others queries. I could rewrite these parts as views. But views doesn't accept parameters and the what is in the where block is long.
I want to define each step like that:
function f_stepn(arg integer) return t_stepn
is
for c in (
select r_stepnMinus1(stepnMinus1.* ) , o.f1,o.f2
from
f_stepnMinus1(arg) stepnMinus1 join othertable o on .....
) loop
pipe row(c)
end loop
In the end I do this this select :
select skip(t.r_stepMinus1.r_stepMinus1.*), skip(t.r_stepMinus1.*), skip(t.*))
where skip in a polymophich function that delete the fields that are records.
P.S.
the function in example in simplified. I can't do that : select r_stepnMinus1(stepnMinus1.* )
I know that I could rewrite the whole definition of r1 inside r2 but I don't want to write 2 times the same thing
A Record is a PL/SQL-only data type and CANNOT be used in SQL statements.
A pipelined function is designed to be used in SQL statements and MUST return a collection that can be used in SQL.
When you return a collection of records, Oracle will implicitly create an OBJECT data-type that reflects the attributes of the record and will return a collection of this object rather than the record (and a collection of object can be used in SQL statements).
However, an OBJECT cannot contain an attribute with a RECORD data type. So when you try to create pipelined function returning a collection of records with a nested record attribute then Oracle cannot implicitly create an Object that reflects the record as the nested record is incompatible and so creating a PIPELINED function will fail.
Either:
Use OBJECT data types created in the SQL scope (instead of records); or
Do not nest records inside records.
Why to I asked this question?
I have a table which as key that have a lot of field. Every time, I'm making a jointure, I miss a field. Therefore I have defined a pipelined function that take the key as an argument so that I am sure that I get only one element when I'm doing a jointure.
But the query take more time now. The table a has an index on some fields but not the table type used by pipelined function. I would like to know if it is possible to created a index on some fields of the table%rowtype
code:
create table a ( a1 integer);
create package p_a
as
type t_a iS TABLE of a%ROWTYPE;
function f(i_a1 integer) return t_a pipelined;
end;
CREATE PACKAGE BODY p_a
AS
CURSOR c_A (i_a1 INTEGER)
RETURN a%ROWTYPE
IS
SELECT t.*
FROM a t
WHERE t.a1 = i_a1;
FUNCTION f (i_a1 INTEGER)
RETURN t_a
PIPELINED
IS
BEGIN
FOR c IN c_a (i_a1)
LOOP
PIPE ROW (c);
END LOOP;
END;
END;
with b as( select 1 b1 from dual) select * from b cross apply (table(p_a.f(b.b1)));
the question
I've tried to index the type table by a field of a table like this
create table a ( a1 integer);
create package p_a2
as
type t_a iS TABLE of a%ROWTYPE index by a.a1%type;
function f(i_a1 integer) return t_a pipelined;
end;
PLS-00315: Implementation restriction: unsupported table index type
Is what I want to do possible. If not how to solve the performance problems mentioned in the introduction?
code
A TYPE is NOT a table and cannot be indexed.
When you do:
create package p_a
as
type t_a iS TABLE of a%ROWTYPE;
end;
/
You are defining a type and the type is a collection data type; an instance of that type is NOT a physical table and but is more like an in-memory array.
When you create a PIPELINED function:
function f(i_a1 integer) return t_a pipelined;
It does NOT return a table; it returns the collection data type.
When you do:
type t_a iS TABLE of a%ROWTYPE index by a.a1%type;
You are NOT creating an index on a table; you are changing to a different collection data type that is an associative array (like a JavaScript object or a Python dictionary) that stores key-value pairs.
An associative array is a PL/SQL data type and (with limited exceptions in later versions for insert, update and delete statements) cannot be used in SQL statements.
When you do:
SELECT * FROM TABLE(SYS.ODCIVARCHAR2LIST('a', 'b', 'c'));
or:
SELECT * FROM TABLE(p_a.f(1));
Then you are passing a collection data type to an SQL statement and the table collection expression TABLE() is treating the collection expression as if it was a table. It is still NOT a table.
If you want to use an index on the table then use the table (without a cursor or a pipeline function):
WITH b (b1) AS (
SELECT 1 FROM DUAL
)
SELECT *
FROM b
CROSS APPLY (
SELECT a.*
FROM a
WHERE a.a1 = b.b1;
);
I think the first line of your question says it all: "key that have a lot of field". If I understand correctly, the table has a primary key that consists of a large number of columns and because of that writing queries becomes a challenge.
It sounds like you're trying to do something pretty complex that should not be an issue at all.
Take a step back and ask yourself - does this need to be the primary key of the table ? Or can you use a surrogate key (identity column, sequence), use that as the primary key and just create a unique index on the set of field that currently make up the primary key. It will (1) simplify your data model and (2) make writing the queries a lot easier.
I want to write a function that returns table records that I can show in my app's grid. My app shall be oblivious to the table's structure. I want it to be still working when someone adds columns to the table or removes them.
In my app it shall look something like this:
myQuery = "select * from table(myfunction)";
...
The function should hence be something like:
CREATE OR REPLACE FUNCTION myfunction RETURN TABLE OF mytable%ROWTYPE AS ...
But RETURN TABLE OF mytable%ROWTYPE is not allowed. One has to create an SQL type.
But CREATE TYPE table_of_mytable_rows IS TABLE OF mytable%ROWTYPE is not allowed either.
So, is there a way to achieve exactly what I want?
As an alternative I thought of working with IDs. This would make my app's code look something like this:
myQuery = "select * from mytable where rowid in (select * from table(myfunction))";
...
And the function would then be
CREATE OR REPLACE FUNCTION myfunction RETURN TABLE OF UROWID AS ...
But again, RETURN TABLE OF UROWID is not allowed. Neither is creating the SQL table type with CREATE TYPE table_of_rowids IS TABLE OF UROWID.
I know I could create an object type resembling the table's primary key columns and then create a table type on this object. Then my app would have to know the table's primary key in order to
myQuery = "select * from mytable where (key1, key2) in (select key1, key2 from table(myfunction))";
...
I would much prefer my function to return table rows or rowids. Is this possible?
I'm currently still working on Oracle 11.2, but I would also be interested in solutions for newer versions.
I have a sample query like below:
INSERT INTO my_gtt_1 (fname, lname) (select fname, lname from users)
In my effort to getting rid of temporary tables I created a package:
create or replace package fname_lname AS
Type fname_lname_rec_type is record (
fname varchar(10),
lname varchar(10)
);
fname_lname_rec fname_lname_rec_type
Type fname_lname_tbl_type is table of fname_lname_rec_type;
function fname_lname_func
(
v_fnam in varchar2,
v_lname in varchar2
)return fname_lname_tbl_type pipelined;
being new to oracle...creating this package took a long time. but now I can not figure out how to get rid of the my_gtt_1
how can i say...
INSERT INTO <newly created package> (select fnma, name from users)
You need to call the pipelined function using the TABLE() syntax:
select *
from table (select fname_lname.fname_lname_func(fnma, name)
from users
where user_id = 123 )
/
Note that the sub-query on USERS must return a single row from that table.
You don't select into packages. You could declare a variable of your table type and bulk collect into that, if you intend to use it in code. I also question your need for a pipelined function. If you're just using the global-temporary table as a springboard for another query, you could probably just use a WITH clause instead. We need a better idea of the bigger picture to recommend a particular technique. Global temporary tables are not inherently bad, either.
I've seen discussions about this in the past, such as here. But I'm wondering if somewhere along the line, maybe 10g or 11g (we are using 11g), ORACLE has introduced any better support for "parameterized views", without needing to litter the database with all sorts of user-defined types and/or cursor definitions or sys_context variables all over.
I'm hoping maybe ORACLE's added support for something that simply "just works", as per the following example in T-SQL:
CREATE FUNCTION [dbo].[getSomeData] (#PRODID ROWID)
RETURNS TABLE AS
RETURN SELECT PRODID, A, B, C, D, E
FROM MY_TABLE
WHERE PRODID = #PRODID
Then just selecting it as so:
SELECT * FROM dbo.getSomeData(23)
No need for SYS_CONTEXT or cursor definitions.
You do need a type so that, when the SQL is parsed, it can determine which columns are going to be returned.
That said, you can easily write a script that will generate type and collection type definitions for one or more tables based on the data in user_tab_columns.
The closest is
create table my_table
(prodid number, a varchar2(1), b varchar2(1),
c varchar2(1), d varchar2(1), e varchar2(1));
create type my_tab_type is object
(prodid number, a varchar2(1), b varchar2(1),
c varchar2(1), d varchar2(1), e varchar2(1))
.
/
create type my_tab_type_coll is table of my_tab_type;
/
create or replace function get_some_data (p_val in number)
return my_tab_type_coll pipelined is
begin
FOR i in (select * from my_table where prodid=p_val) loop
pipe row(my_tab_type(i.prodid,i.a,i.b,i.c,i.d,i.e));
end loop;
return;
end;
/
SELECT * FROM table(get_Some_Data(3));
It is possible to define a kind of "parametrized" views in Oracle.
The steps are:
Define a package containing as public members that are in fact the needed parameters (there is no need for functions or procedures in that package),
Define a view that is based on that package members.
To use this mechanism one user should:
open a session,
assign the desired values to that package members,
SELECT data from the view,
do other stuff or close the session.
REMARK: it is essential for the user to do all the three steps in only one session as the package members scope is exactly a session.
There are TWO types of table-valued functions in SQL SERVER:
Inline table-valued function: For an inline table-valued function, there is no function body; the table is the result set of a single SELECT statement. This type can be named as
'parameterized view' and it has no equivalent in ORACLE as I know.
Multistatement table-valued function: For a multistatement table-valued function, the function body, defined in a BEGIN...END block, contains a series of Transact-SQL statements that build and insert rows into the table that will be returned.
The above sample (By Gary Myers) creates a table function of the second type and it is NOT a 'parameterized view'.