Using an Oracle Table Type in IN-clause - compile fails - oracle

Simply trying to get a cursor back for the ids that I specify.
CREATE OR REPLACE PACKAGE some_package AS
TYPE t_cursor IS REF CURSOR;
TYPE t_id_table IS TABLE OF NVARCHAR(38) INDEX BY PLS_INTEGER;
PROCEDURE someentity_select(
p_ids IN t_id_table,
p_results OUT t_cursor);
END;
CREATE OR REPLACE PACKAGE BODY some_package AS
PROCEDURE someentity_select(
p_ids IN t_guid_table,
p_results OUT t_cursor)
IS
BEGIN
OPEN p_results FOR
SELECT *
FROM someschema.someentity
WHERE id IN (SELECT column_value FROM TABLE(p_ids)); - fails here
END;
END;
Note: someschema.someentity.id is a NVARCHAR2(38)
PL/SQL: ORA-00382: expression is of wrong type
PL/SQL: ORA-22905: cannot access rows from a non-nested table item
Where am I going wrong?

In Oracle versions prior to 12.2 you can only SELECT from a collection type that is defined in the database via a CREATE TYPE statement, not an associative array:
CREATE TYPE t_id_table IS TABLE OF NVARCHAR(38);
CREATE OR REPLACE PACKAGE some_package AS
PROCEDURE someentity_select(
p_ids IN t_guid_table,
p_results OUT SYS_REFCURSOR);
END;
CREATE OR REPLACE PACKAGE BODY some_package AS
PROCEDURE someentity_select(
p_ids IN t_guid_table,
p_results OUT SYS_REFCURSOR)
IS
BEGIN
OPEN p_results FOR
SELECT *
FROM someschema.someentity
WHERE id IN (SELECT column_value FROM TABLE(p_ids));
END;
END;

This is an index-by table, which is a PL/SQL type.
You can only use SQL types in the SQL engine of Oracle. Or PL/SQL types, that Oracle can hack around to look like SQL types.
You can have a simple array-like collection and use it as a result. (no index by)
type TGuidList is table of NVarchar(38);
But the best compatibility and stability, you get by declaring it as a global SQL type and use that inside your package:
create type TGuidList is table of NVarchar(38);
Edit: You will not need an NVarChar for a GUID, will you? A good ol' VarChar should do the trick just fine.

Related

Oracle PL/SQL Developer: Return %RowType from Package Procedure

i'm kind of new to Oracle Pl\SQL. I was just trying to create a simple Package with a procedure that returns a set of object id's; the code is as follows:
--Package Spec
CREATE OR REPLACE PACKAGE TEST IS
--GET OBJECT ID'S FROM CONTROL TABLE
PROCEDURE get_object_id_control(p_obj_id OUT abc_table%ROWTYPE);
END;
--Package Body
PROCEDURE get_object_id_control(p_obj_id OUT abc_table%ROWTYPE) AS
BEGIN
SELECT object_id
INTO p_obj_id
FROM abc_table
WHERE fec_proc IS NULL;
END;
I get Error: PL/SQL: ORA-00913: too many values. Is this the correct way for returning multiple values of same data type, or is there a better approach. Thanks in advance.
You can create a custom table type and set the out parameter of the procedure to that type.
CREATE TABLE ABC_TABLE(ID varchar2(100));
create or replace type abc_tab is table of varchar2(100);
/
CREATE OR REPLACE PACKAGE TEST IS
PROCEDURE get_object_id_control(p_obj_id OUT abc_tab);
END;
/
CREATE OR REPLACE PACKAGE BODY TEST IS
PROCEDURE get_object_id_control(p_obj_id OUT abc_tab) AS
BEGIN
SELECT id
bulk collect INTO p_obj_id
FROM abc_table;
END;
END;
/
Then you can call it like so:
declare
v abc_tab;
begin
TEST.get_object_id_control(p_obj_id => v);
for i in v.first..v.last loop
dbms_output.put_line(v(i));
end loop;
end;
/
Similar to GurV's answer (since he beat me by like 30 seconds...), you can use a PL/SQL object type as well. You do not need the CREATE TYPE statement if you don't need to reference the type in SQL.
--Package Spec
CREATE OR REPLACE PACKAGE TEST AS
TYPE id_table_type IS TABLE OF NUMBER;
--GET OBJECT ID'S FROM CONTROL TABLE
PROCEDURE get_object_id_control(p_obj_id_list OUT id_table_type);
END;
--Package Body
CREATE OR REPLACE PACKAGE BODY TEST AS
PROCEDURE get_object_id_control(p_obj_id_list OUT id_table_type) AS
BEGIN
SELECT object_id
BULK COLLECT INTO p_obj_id_list
FROM abc_table
WHERE fec_proc IS NULL;
END;
END;
To use it:
DECLARE
l_id_list test.id_table_type;
BEGIN
test.get_object_id_control (p_obj_id_list => l_id_list);
FOR i IN l_id_list.FIRST .. l_id_list.LAST LOOP
DBMS_OUTPUT.put_line (l_id_list (i));
END LOOP;
END;

PL/SQL array into refcursor to return

I'm new to PL/SQL. I need to convert an array into a refcursor to return the record set. this is how SP looks like:
PROCEDURE EXAMPLE(p_recordset OUT SYS_REFCURSOR) AS
TYPE COUNTRY_ARR IS TABLE OF VARCHAR2(60) INDEX BY BINARY_INTEGER;
V_COUNTRY_ARR COUNTRY_ARR;
BEGIN
V_COUNTRY_ARR(1) := 'US';
V_COUNTRY_ARR(2) := 'AUS';
V_COUNTRY_ARR(3) := 'NA';
OPEN p_recordset FOR SELECT * FROM TABLE(CAST(V_COUNTRY_ARR AS COUNTRY_ARR));
END EXAMPLE;
Gives me an error saying 'Error(86,68): PL/SQL: ORA-00902: invalid
datatype'
Thank you in advance!
You are trying to cast to a PL/SQL type. That cannot be done until Oracle 12c (and, even then, the type would need to be defined in a package specification, I believe).
In Oracle 11g, you need to create COUNTRY_ARR as a SQL type with the CREATE TYPE command. Then your cast will work.
I.e., (outside of your PL/SQL package)
CREATE TYPE country_arr AS TABLE OF VARCHAR2(60);
Sorry I don't have an Oracle database handy to give more complete/exact syntax.

How to create an oracle stored procedure with an array parameter

I créated an Oracle stored procedure into a package. I did like this:
CREATE OR REPLACE PACKAGE PACKFACE IS
TYPE LIST_IDS IS TABLE OF INT INDEX BY BINARY_INTEGER;
PROCEDURE P_SELECT_IDBFRIENDS (CONSULTA OUT SYS_REFCURSOR,COD_US IN INT,IDS_NOT IN LIST_IDS);
END;
And the body of the package is:
CREATE OR REPLACE PACKAGE BODY PACKFACE IS
PROCEDURE P_SELECT_IDBFRIENDS (CONSULTA OUT SYS_REFCURSOR,COD_US IN INT, IDS_NOT IN LIST_IDS) IS
BEGIN
OPEN CONSULTA FOR
SELECT ID_US1,ID_US2 FROM T_FRIENDSHIP WHERE ID_US1=COD_US AND ID_US2 NOT IN (SELECT COLUMN_VALUE FROM TABLE(IDS_NOT));
END;
END;
/
These is ok in my Oracle 12c server, but I did the same code in Oracle 11g appeared an error, cannot access rows from a non-nested table ítem. What would be the solution? Thanks in advance
After this problem was fixed. Appears other one my Python code was broken. I have this procedure:
def select_ids(self,cod_us,ids_not):
lista = []
try:
cursor = self.__cursor.var(cx_Oracle.CURSOR)
varray = self.__cursor.arrayvar(cx_Oracle.NUMBER,ids_not)
l_query = self.__cursor.callproc("PROC_SELECT_IDS_ENT_AMISTADES", [cursor, cod_us, varray])
lista = l_query[0]
return lista
except cx_Oracle.DatabaseError as ex:
error, = ex.args
print(error.message)
return lista
PLS-00306 wrong number or type of arguments in call to a procedure. It was ok using Oracle 12c. Thanks in advance again.
You cannot use a collection type defined in PL/SQL in an SQL query in Oracle 11.
If you want to use a collection in both SQL and PL/SQL then you will have to define it in SQL:
CREATE TYPE LIST_IDS IS TABLE OF INT;
Then you can do:
CREATE OR REPLACE PACKAGE PACKFACE IS
PROCEDURE P_SELECT_IDBFRIENDS (
CONSULTA OUT SYS_REFCURSOR,
COD_US IN INT,
IDS_NOT IN LIST_IDS
);
END;
/
SHOW ERRORS;
CREATE OR REPLACE PACKAGE BODY PACKFACE IS
PROCEDURE P_SELECT_IDBFRIENDS (
CONSULTA OUT SYS_REFCURSOR,
COD_US IN INT,
IDS_NOT IN LIST_IDS
)
IS
BEGIN
OPEN CONSULTA FOR
SELECT ID_US1,ID_US2
FROM T_FRIENDSHIP
WHERE ID_US1=COD_US
AND ID_US2 NOT MEMBER OF IDS_NOT;
END;
END;
/
SHOW ERRORS;
First of all you should to create an oracle type like this:
CREATE OR REPLACE TYPE LIST_IDS AS TABLE OF INT;
And after that you should to create the package with the procedure and a nested table in parameter like this:
CREATE OR REPLACE PACKAGE PACKFACE IS
TYPE LISTADO_IDS IS TABLE OF INT INDEX BY PLS_INTEGER;
PROCEDURE P_SELECT_IDBFRIENDS (CONSULTA OUT SYS_REFCURSOR,COD_US IN INT,IDS_NOT IN LISTADO_IDS);
END;
Finally you should create the body. You pass the data to nested table from oracle type like this:
CREATE OR REPLACE PACKAGE BODY PACKFACE IS
PROCEDURE P_SELECT_IDBFRIENDS (CONSULTA OUT SYS_REFCURSOR,COD_US IN INT, IDS_NOT IN LISTADO_IDS)
IS
num_array FACEBOOK.LIST_IDS;
BEGIN
num_array:=LIST_IDS();
for i in 1 .. IDS_NOT.count
loop
num_array.extend(1);
num_array(i) := IDS_NOT(i);
end loop;
OPEN CONSULTA FOR
SELECT ID_US1,ID_US2 FROM T_FRIENDSHIP WHERE ID_US1=COD_US AND ID_US2 NOT IN (SELECT COLUMN_VALUE FROM TABLE(num_array));
END;
END;
I did that and I got the solution to the python error wrong number or type parameters. Good luck.

Oracle stored procedure to return a list of ids

I'm trying to convert an sql stored procedure to oracle. It's a very trivial procedure. It just returns a list of ids from a table, which is very easy to do in sql.
SQL
CREATE PROCEDURE [dbo].[tspInstalledLanguages]
AS
BEGIN
SELECT language_id from languages
END
I'm a complete novice when it comes to oracle, and I've learned that it's not as straight forward as this.
I've tried the following:
Oracle
CREATE OR REPLACE PROCEDURE tspInstalledLanguages
AS
BEGIN
SELECT LANGUAGE_ID FROM LANGUAGES;
END;
With no luck.
I get a message that it's expecting select into
It is.
Example:
DECLARE
v_authName author.author_last_name%type;
BEGIN
SELECT author_last_name
INTO v_authName
FROM author
WHERE author_key = 'A103';
dbms_output.put_line('Name: '||v_authName);
END;
/
So using your code:
CREATE OR REPLACE PROCEDURE tspInstalledLanguages
IS
v_language NUMBER;
BEGIN
SELECT LANGUAGE_ID
INTO v_language
FROM LANGUAGES;
dbms_output.put_line(v_language);
END;
/

How to populate an array in an oracle stored procedure?

How to use array( Varray) in store procedure. Actually,i have make a stored procedure from which i retrieve a list of elements.
For example:
create or replace procedure GetTargetFields ( fileformat in varchar2,
filefields out Varray(4) )
IS
BEGIN
SELECT id
INTO filefields
FROM tablename;
END;
use BULK COLLECT INTO:
SQL> CREATE OR REPLACE TYPE vrray_4 AS VARRAY(4) OF VARCHAR2(10);
2 /
Type created
SQL> CREATE OR REPLACE PROCEDURE GetTargetFields(fileformat IN VARCHAR2,
2 filefields OUT vrray_4) IS
3 BEGIN
4 SELECT dummy BULK COLLECT INTO filefields FROM dual;
5 END;
6 /
Procedure created
SQL> DECLARE
2 x vrray_4;
3 BEGIN
4 GetTargetFields(NULL, x);
5 END;
6 /
PL/SQL procedure successfully completed
Also make sure that your query doesn't return more than 4 rows (for a VARRAY(4)) or you will run into ORA-22165
Niraj. You should use the principles Vincent provided, but I suggest you use nested table type instead of varray in case you don't need exactly varray type in your logic. This will save you from ORA-22165 error if the query returns more then 4 rows - nested tabled will be automatically expanded to the size needed. You define nested table type as follows:
declare
type TStrTab is table of varchar2(10);
fStrTab TStrTab := TStrTab();
begin
select ... bulk collect into fStrTab from...
end;
More information about PL/SQL collection types can be found in official Oracle PL-SQL User's Guide and Reference Chapter 5.
Two things:
You need to declare a named type -- you can't use VARRAY directly in a parameter declaration. (Unless this has changed in 11g.)
You need to use BULK COLLECT to use a single query to populate a collection.
Example:
CREATE TYPE fieldlist AS VARRAY(4) OF NUMBER;
CREATE PROCEDURE GetTargetFields( filefields OUT fieldlist )
AS
BEGIN
SELECT id BULK COLLECT INTO filefields FROM tablename;
END;

Resources