is it possible to pass arrays as parameters in procedures? - oracle

i'm trying to pass an array as a parameter in my procedure but i keep getting a command unknown error
code
SET SERVEROUTPUT ON;
TYPE pourcentage_remise IS TABLE OF NUMBER INDEX BY commandeproduit.ref_produit%type;
CREATE OR REPLACE PROCEDURE remise_produit( pourcent IN pourcentage_remise,
ref_comm IN commande.ref_commande%type,
c_ht OUT commandeproduit.prix_ht%type,
c_ttc OUT commandeproduit.prix_ttc%type)
IS
CURSOR p_curs IS
SELECT ref_produit, prix_ttc, prix_ht FROM commandeproduit WHERE concerne = ref_comm ;
ref commandeproduit.ref_produit%type;
ttc commandeproduit.prix_ttc%type;
ht commandeproduit.prix_ht%type;
BEGIN
open p_curs;
LOOP
FETCH p_curs into ref, ttc, ht;
EXIT WHEN p_curs%notfound;
dbms_output.put_line(ref, ' ',ht, ' ', ttc);
IF pourcent(ref) THEN
ttc := ttc - ttc * pourcent(ref);
ht := ht - ttc * pourcent(ref);
INSERT INTO commandeproduit(prix_ht, prix_ttc) VALUES(ht, ttc) WHERE concerne = ref_comm AND ref_produit = ref;
END IF;
dbms_output.put_line(ref, ' ',ht, ' ', ttc);
END LOOP;
close p_curs;
END remise_produit;
/
procedure call
DECLARE
pourcentage pourcentage_remise;
reference commande.ref_commande%type :=1;
BEGIN
pourcentage('A01') :=0.15;
pourcentage('B15') :=0.2;
remise_produit(pourcentage, reference);
END;
/
the table
the error in french which means command unknown
please help

Your syntax error is on the declaration of your type so the rest of your code isn't really needed.
TYPE pourcentage_remise IS TABLE OF NUMBER INDEX BY commandeproduit.ref_produit%type;
Several problems
If you are trying to declare a type in SQL, you'd need to use a CREATE TYPE so you're missing the CREATE.
If you are trying to declare a table type in SQL, you can't use an associative array. You'd realistically want a nested table instead.
If you are trying to declare a PL/SQL type, your statement would need to be in a PL/SQL block. You could declare a package that contains an associative array type.
If you want to declare a nested table type in SQL
CREATE TYPE pourcentage_remise IS TABLE OF NUMBER;
If you want to declare an associative array in a PL/SQL package
CREATE OR REPLACE PACKAGE my_collection_pkg
AS
TYPE pourcentage_remise IS TABLE OF NUMBER INDEX BY commandeproduit.ref_produit%type;
END;
If you want to use a nested table type, that changes how you need to initialize your associative array. It should change how you reference elements of that array, but I'm confused by your code. Your procedure appears to be using a numeric index to access an element of the associative array which doesn't make sense if the associative array uses a string as the index.

Related

PLSQL : Using Member of with Nested Table of type NUMBER - PLS-00330: invalid use of type name or subtype name

Working with collections in plsql for the first time.
Declaraton of nested table :
TYPE nt_orders IS TABLE OF NUMBER
INDEX BY BINARY_INTEGER;
nt_invc_orders nt_orders;
Where I am using member of
IF( 12345 member of nt_orders) THEN
nt_scb_temp_objects(i).invc_ref := p_invc_ref;
END IF;
NOTE : For now, I have entered 12345 as my search, in reality this will be a variable(of Number type) stored value.
ERROR : PLS-00330: invalid use of type name or subtype name
Your collection is actually not a nested table, it's an associative array. You should remove INDEX BY BINARY_INTEGER; to make it a nested table. Moreover, MEMBER OF function doesn't work with associative arrays. Second problem is you are searching for the element with the collection type - nt_orders as the right argument, which is wrong. It should be the nested table variable.
declare
TYPE nt_orders IS TABLE OF NUMBER;
nt_invc_orders nt_orders := nt_orders(12345);
begin
IF 12345 member of nt_invc_orders THEN
dbms_output.put_line('found');
else
dbms_output.put_line('not found');
END IF;
end;
/
Output
found
PL/SQL procedure successfully completed.

How to pass an array type in plsql package specification?

I am new to plsql. I am trying to put two scripts under the same package. These scripts deal with arrays. How do I pass an array into the procedure? If I am to declare the array, do I do it in the specification or the body? I am trying this right now but it doesn't work.
CREATE PACKAGE cop_cow_script AS
PROCEDURE COP_COW_DATALOAD_V2(arr_claims VARRAY(15000) OF VARCHAR2(10), arr_sql VARRAY(500) OF VARCHAR2(1000));
END cop_cow_script;
As you see I want to pass in those two arrays.
You need to declare types in the package specification and use them as parameter types in the procedure declaration:
CREATE OR REPLACE PACKAGE cop_cow_script AS
TYPE arr_claims_t IS VARRAY(15000) OF VARCHAR2(10);
TYPE arr_sql_t IS VARRAY(500) OF VARCHAR2(1000);
PROCEDURE COP_COW_DATALOAD_V2(arr_claims arr_claims_t, arr_sql arr_sql_t);
END cop_cow_script;
/
EDIT - below is an example of the package body - the procedure loops through elements of it's first parameter and prints them using DBMS_OUTPUT.PUT_LINE
PROCEDURE COP_COW_DATALOAD_V2(arr_claims arr_claims_t, arr_sql arr_sql_t)
IS
BEGIN
FOR i IN arr_claims.FIRST .. arr_claims.LAST
LOOP
DBMS_OUTPUT.PUT_LINE( arr_claims( i ) );
END LOOP;
END;
END cop_cow_script;
/
An then you can initialize them and pass to the procedure invocation for example in this way (this is an anonymous block that declares two variables, initializes them and invokes the procedure passing both parameters to it:
DECLARE
my_array1 cop_cow_script.arr_claims_t := cop_cow_script.arr_claims_t();
my_array2 cop_cow_script.arr_sql_t := cop_cow_script.arr_sql_t();
BEGIN
my_array1.extend;
my_array1( 1 ) := 'string 1';
my_array2.extend;
my_array2( 1 ) := 'string 2';
cop_cow_script.COP_COW_DATALOAD_V2( my_array1, my_array2 );
END;
/
First off, I'm hard-pressed to imagine a situation where you'd actually want to use a PL/SQL varray rather than a nested table or an associative array. There really isn't a benefit to declaring a fixed size collection.
Whatever sort of collection type you want, you'd need to declare the collection type either in the package specification or at the SQL level (depending on the type of collection) separate from the procedure specification
CREATE PACKAGE cop_cow_script AS
TYPE arr_claims IS varray(15000) of varchar(10);
TYPE arr_sql IS varray(500) of varchar(1000);
PROCEDURE COP_COW_DATALOAD_V2(p_claims arr_claims,
p_sql arr_sql);
END cop_cow_script;

Casting between PL/SQL collections of strings of different length

I need to assign a collection (a nested table) of varchar2(10) elements to another collection variable the element type of which is varchar2(20). Is there any way to do this, better than building the new collection row by row in a loop?
declare
type TList10 is table of varchar2(10);
type TList20 is table of varchar2(20);
vList10 TList10 := TList10('Test1', 'Test2');
vList20 TList20;
begin
-- This raises PLS-00382
vList20 := vList10;
end;
No, you can't do that. vList10 is of a different type than vList20 so you cannot assign one to the other. You will have to loop through the contents.
this is probably mostly syntactic sugar (although Oracle: Bulk Collect performance): If your types are not local you could use a one-liner:
select column_value bulk collect into vList20 from table(vList10);

Listagg function with PLSQL collection

I have a PL/SQL collection of following type
type p_typ_str_tab is table of varchar2(4000) index by pls_integer;
I would like to aggregate the values into a single string with a simple inline function like LISTAGG without writing any custom functions or for loops. All examples of LISTAGG don't show how to use PL/SQL collections. I'm using Oracle 11g R2. Is this possible?
To be able to use LISTAGG function with a collection, the collection must be declared as nested table not as an associative array and must be created as sql type (schema object) because it's not possible to use pl/sql type in a select statement. To that end you might do the following:
--- create a nested table type
SQL> create or replace type t_tb_type is table of number;
2 /
Type created
--- and use it as follows
SQL> select listagg(column_value, ',') within group(order by column_value) res
2 from table(t_tb_type(1,2,3)) -- or call the function that returns data of
3 / -- t_tb_type type
RES
-------
1,2,3
Otherwise the loop is your only choice.
LISTAGG is an analytic SQL function, and those don't operate against PL/SQL collections, but cursors/rowsets.
So, in short, no, it's not possible.
That said, iterating over a PL/SQL table to build a concatenated string is trivial:
l_new_string := null;
for i in str_tab.first .. str_tab.last
loop
if str_tab(i) is not null then
l_new_string := str_tab(i) || ', ';
end if;
end loop;
-- trim off the trailing comma and space
l_new_string := substr(l_new_string, 1, length(l_new_string) - 2);

How to call a procedure with associative arrays in Oracle from Java

I have an stored procedure that looks like this:
TYPE ref_cursor IS REF CURSOR;
TYPE parametro IS RECORD (
nombre VARCHAR2(50), -- I want to remove this value and make it the key of the table instead.
valor VARCHAR2(32000),
tipo VARCHAR2(1),
sentencia VARCHAR2(32000)
);
TYPE parametros IS TABLE OF parametro INDEX BY VARCHAR2(50);
PROCEDURE build_cursor (
params IN parametros
results OUT ref_cursor
);
And from the build_cursor procedure, I want to be able to access to the contents of the table by its key.
parametros('key');
However, I don't know how to build an associative array from Java, I have seen only examples of simple arrays, i.e: TYPE parametros IS TABLE OF parametro;
How can I call the build_cursor procedure from java?
I read this: How to call oracle stored procedure which include user-defined type in java? but I don't know what changes do I have to make to his java example for creating the associative array; Where do I put the Key of the current element?
This is a working test from Oracle.
params('key').nombre := 'key'; -- I want this to be removed because it's the key.
params('key').valor := 'Roger';
params('key').tipo := 'V';
params('key').sentencia := 'Something';
-- Call the procedure
pk_sql_utils.build_cursor(
params => params,
results => :results
);
Only SQL objects can be referenced by jdbc, not PL/SQL objects. Associative arrays are PL/SQL objects, so you won't be able to "see" them from jdbc.
You could use a wrapper PL/SQL function with SQL objects (an associative array is analogous to one nested table of indexes and one nested table of values).
You could also use a temporary table: jdbc batch inserts into the temp table, a wrapper procedure (or PL/SQL block) reads the temp table and calls your procedure.

Resources