Hi I need to retrieve a parameter value where the parameter name is in different parameter.
Lets say the proc is as below
PROCEDURE findValue
(
p_date IN VARCHAR2,
p_name IN VARCHAR2,
p_class IN VARCHAR2,
p_paramname IN VARCHAR2,
)
IS
Now lets say i want to pass p_paramname as p_date and further use the value of p_date parameter in PL/SQL block, how do i use it ?
If you can afford PL/SQL table inputs then try the logic below:
CREATE TYPE param_tbl_type IS TABLE OF VARCHAR2(255);
CREATE OR REPLACE FUNCTION
(p_param_names param_tbl_type,
p_param_values param_tbl_type)
RETURN VARCHAR2
IS
l_dyn_func_str VARCHAR2(4000);
l_ret_val VARCHAR2(4000);
vCursor integer;
fdbk PLS_INTEGER;
BEGIN
-- make sure you have equal number of params and currespondin values.
-- Index much match too. i.e. if p_param_names(0) is 'p_currency' then p_param_value(0) must be 'USD' (value of currency)
IF p_param_names.COUNT <> p_param_values.COUNT THEN
raise_application_error(-2000,'Incorrect number of arguments');
END IF;
-- use this variable to generate anonymous block code
l_dyn_fun_str:='BEGIN :retval := function_tobe_called (';
-- loop through each parameter and add to the parameter list
FOR i in 1..p_param_names.COUNT LOOP
IF i=0 THEN
l_dyn_fun_str:=l_dyn_fun_str||':'||l_param_name(i);
ELSE
l_dyn_fun_str:=l_dyn_fun_str||','||':'||l_param_name(i);
END IF;
END LOOP;
l_dyn_fun_str:=l_dyn_fun_str||'); END;'
-- open cursor and associate with function call string (l_dyn_fun_str)
vCursor:=DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(vCursor,l_dyn_fun_str);
-- loop through parameter values and associate them with bind variables
DBMS_SQL.BIND_VARIABLE(vCursor,':retval',l_ret_val);
FOR j in 1..p_param_values.COUNT LOOP
DBMS_SQL.BIND_VARIABLE(vCursor, ':'||l_param_names(j), l_param_values(j));
END LOOP;
-- execute function
fdbk := DBMS_SQL.EXECUTE (vCursor);
-- get output of function
DBMS_SQL.VARIABLE_VALUE (vCursor, 'retval', l_ret_val);
RETURN l_ret_val;
END;
Note: The code syntax may not be perfect but the pseudo should still work for your requirement.
Just check the value of P_PARAMNAME against the possible parameter names and branch accordingly. E.g.,
CREATE OR REPLACE PROCEDURE matt_test1 (p_date IN VARCHAR2,
p_name IN VARCHAR2,
p_class IN VARCHAR2,
p_paramname IN VARCHAR2) IS
BEGIN
CASE p_paramname
WHEN 'P_NAME' THEN
DBMS_OUTPUT.put_line ('Did something with P_NAME. Value was ' || p_name);
WHEN 'P_DATE' THEN
DBMS_OUTPUT.put_line ('Did something with P_DATE. Value was ' || p_date);
WHEN 'P_CLASS' THEN
DBMS_OUTPUT.put_line ('Did something with P_CLASS. Value was ' || p_class);
ELSE
raise_application_error (-20001, 'Invalid parameter name: ' || p_paramname);
END CASE;
END matt_test1;
begin
matt_test1(SYSDATE,'Fred','English 101', 'P_DATE');
matt_test1(SYSDATE,'Fred','English 101', 'P_NAME');
matt_test1(SYSDATE,'Fred','English 101', 'P_CLASS');
end;
Output:
Did something with P_DATE. Value was 11-Aug-2015
Did something with P_NAME. Value was Fred
Did something with P_CLASS. Value was English 101
Related
i need the values of a json_array. I tried this:
DECLARE
l_stuff json_array_t;
BEGIN
l_stuff := json_array_t ('["Stirfry", "Yogurt", "Apple"] ');
FOR indx IN 0 .. l_stuff.get_size - 1
LOOP
INSERT INTO t_taböe (name, type)
VALUES(l_stuff.get(i), 'TEXT');
END LOOP;
END;
You are passing the position as i instead of indx; but you need a string so use get_string(indx) as #Sayan said.
But if you try to use that directly in an insert you'll get "ORA-40573: Invalid use of PL/SQL JSON object type" because of a still-outstanding (as far as I know) bug.
To work around that you can assign the string to a variable first:
l_name := l_stuff.get_string(indx);
INSERT INTO t_taböe (name, type)
VALUES(l_name, 'TEXT');
db<>fiddle
You do not need PL/SQL and can do it in a single SQL statement:
INSERT INTO t_taböe (name, type)
SELECT value,
'TEXT'
FROM JSON_TABLE(
'["Stirfry","Yogurt","Apple"]',
'$[*]'
COLUMNS (
value VARCHAR2(50) PATH '$'
)
);
db<>fiddle here
First convert the JSON array into an ordinary PL/SQL array, then use a bulk insert.
Here is a reproducible example:
create table tab (name varchar2 (8), type varchar2 (8))
/
declare
type namelist is table of varchar2(8) index by pls_integer;
names namelist;
arr json_array_t := json_array_t ('["Stirfry", "Yogurt", "Apple"]');
begin
for idx in 1..arr.get_size loop
names(idx) := arr.get_string(idx-1);
end loop;
forall idx in indices of names
insert into tab (name, type) values (names(idx), 'TEXT');
end;
/
The query and outcomes:
select * from tab
/
NAME TYPE
-------- --------
Stirfry TEXT
Yogurt TEXT
Apple TEXT
Just use get_string:
DECLARE
l_stuff json_array_t;
BEGIN
l_stuff := json_array_t ('["Stirfry", "Yogurt", "Apple"] ');
FOR indx IN 0 .. l_stuff.get_size - 1
LOOP
--INSERT INTO t_taböe (name, type)
-- VALUES(l_stuff.get_string(indx), 'TEXT');
dbms_output.put_line(l_stuff.get_string(indx));
END LOOP;
END;
I'm kinda new into pgplsql and so far I have to create a function that loops an array that is received as a function.
The main idea of the function is to insert new records into a table that maps each id contained in the array received with a new formatted id, the format depends on the second parameter received and return the table "idsTable".
The problem is that when I try to create the function it sends me an error:
ERROR: loop variable of FOREACH must be a known variable or list of variables
LINE 38: FOREACH objectid IN ARRAY idsList LOOP
I'm not sure if I have to declare the objectid variable cause in the examples that I have seen they didn't.
So far I have this:
CREATE OR REPLACE FUNCTION createId(idsList varchar[], objectType varchar)
RETURNS TABLE(original_id varchar, new_id char) as
$$
BEGIN
IF LOWER(objectType) = 'global' THEN
FOREACH objectid IN ARRAY idsList LOOP
INSERT INTO idsTable(original_id, new_id)
VALUES(objectid, 'GID'||nextval('mapSquema.globalid')::TEXT);
END LOOP;
ELSE
FOREACH objectid IN ARRAY idsList LOOP
INSERT INTO idsTable(original_id, new_id)
VALUES(objectid, 'ORG'||nextval('mapSquema.globalid')::TEXT);
END LOOP;
END IF;
END;
$$ LANGUAGE plpgsql;
Any ideas of what could be wrong?
edit: I haven't add the part where the idsTable is returned.
Unrelated, but: you don't really need a loop for that. And you can simplify the function by only writing the INSERT once. You also forgot to return something from your function. As it is declared as returns table that is required:
CREATE OR REPLACE FUNCTION createid(idslist varchar[], objecttype varchar)
RETURNS TABLE(original_id varchar, new_id varchar) as
$$
declare
l_prefix text;
BEGIN
IF LOWER(objectType) = 'global' THEN
l_prefix := 'GID';
ELSE
l_prefix := 'ORG';
END IF;
RETURN QUERY --<< return the result of the insert
INSERT INTO idstable(original_id, new_id)
select t.x, l_prefix||nextval('mapSquema.globalid')::TEXT
from unnest(idslist) as t(x)
returning *
END;
$$ LANGUAGE plpgsql;
I am attempting to pass a java array into my stored procedure and updating records with the values in the array. Currently when I attempt to execute and test the stored procedure I am running into
Error:ORA-06531: Reference to uninitialized collection.
Can anybody push me in the right direction or help me clean up my code. Below is the package spec followed the body.
CREATE OR REPLACE package AOMS.test_array1 as
type t1 is record (
s1 varchar2(1),
i_part_no varchar2(20),
i_itc varchar2(20),
s2 varchar2(1),
l_part_no varchar2(20));
type tab1 is table of t1 ;
tab2 tab1;
Here is the body.
CREATE OR REPLACE PACKAGE BODY AOMS.TEST_ARRAY1 AS
I_ARRAY varchar2(1000);
PROCEDURE test_array2(i_array IN tab2%TYPE) AS
l_s1 VARCHAR2(50);
l_part_no1 VARCHAR2(50);
l_itc varchar2(50);
l_s2 varchar2(50);
l_part_no2 varchar2(50);
BEGIN
FOR x IN i_array.first .. i_array.last
LOOP
l_s1 := i_array(x).s1;
l_part_no1 := i_array(x).i_part_no;
l_itc := i_array(x).i_itc;
l_s2 := i_array(x).s2;
l_part_no2 := i_array(x).l_part_no;
UPDATE replacement_parts
SET frst_src = l_s1,
frst_part_no = l_part_no1,
ITC = l_itc,
last_src = l_s2,
last_part_no = l_part_no2
WHERE
frst_src = 'P'
AND frst_part_no = '96424447 ';
COMMIT;
END LOOP;
END test_array2;
END test_array1;
/
I'm using Toad so when I call the procedure I just right click and execute and enter in my params. Here is the anonymous block code that gets generated when I attempt to execute.
DECLARE
I_ARRAY AOMS.TEST_ARRAY1.tab2%type;
BEGIN
-- I_ARRAY := NULL; Modify the code to initialize this parameter
AOMS.TEST_ARRAY1.TEST_ARRAY2 ( I_ARRAY );
COMMIT;
END;
You have some issues both in the package and in the procedure call.
This should work:
CREATE OR REPLACE PACKAGE test_array1 AS
TYPE t1 IS RECORD
(
s1 VARCHAR2(1),
i_part_no VARCHAR2(20),
i_itc VARCHAR2(20),
s2 VARCHAR2(1),
l_part_no VARCHAR2(20)
);
TYPE tab1 IS TABLE OF t1;
tab2 tab1;
PROCEDURE test_array2(i_array IN tab1);
END test_array1;
CREATE OR REPLACE PACKAGE BODY TEST_ARRAY1 AS
I_ARRAY VARCHAR2(1000);
PROCEDURE test_array2(i_array IN tab1) IS
l_s1 VARCHAR2(50);
l_part_no1 VARCHAR2(50);
l_itc VARCHAR2(50);
l_s2 VARCHAR2(50);
l_part_no2 VARCHAR2(50);
BEGIN
IF i_array.COUNT > 0
THEN
FOR x IN i_array.FIRST .. i_array.LAST
LOOP
l_s1 := i_array(x).s1;
l_part_no1 := i_array(x).i_part_no;
l_itc := i_array(x).i_itc;
l_s2 := i_array(x).s2;
l_part_no2 := i_array(x).l_part_no;
UPDATE replacement_parts
SET frst_src = l_s1,
frst_part_no = l_part_no1,
ITC = l_itc,
last_src = l_s2,
last_part_no = l_part_no2
WHERE frst_src = 'P'
AND frst_part_no = '96424447 ';
COMMIT;
END LOOP;
END IF;
END test_array2;
END test_array1;
/
The call:
DECLARE
I_ARRAY TEST_ARRAY1.tab1;
BEGIN
I_ARRAY := TEST_ARRAY1.tab1();
TEST_ARRAY1.TEST_ARRAY2 ( I_ARRAY );
COMMIT;
END;
The changes I made:
you define a type in your package, then use something like variable%type to declare the procedure, while you can simply use the type.
in the package, while scanning a collection, it's better to check if the collection has values before trying to use collection.first. Trying to access the .first on an empty collection can lead to an issue.
in the caller, you need to initialize the collection the way I showed to avoid the error you are having
As an aside, you should better try to use more explanatory names for variables, types, procedures, packages to avoid confusion between different objects.
Another thing: you have a commit inside a loop; this means that, keeping aside performances, if the first, say, 3 records are updated and then you have an error, you commit 3 updates; is this really what you need? Also, this way the commit in the caller is unuseful.
I want to store the content of a cursor in an associative array (Table index by binary_integer). But in the same array I also want to store an additional variable, say a boolean.
My cursor has n elements per row and the array is defined to have n+1 elements (n with the same %type as the cursorelements), the last one being the boolean.
What I whant is something like this
for cursorrow in cursor(...)
loop
array(row i) := cursorrow, boolean_variable;
end loop;
|1|2|...|n|n+1| := |1|2|...|n|, |1|
Unfortunately I can't get it to work.
Anybody knows how to do it?
How about defining a composite record type consisting of a %rowtype record and a Boolean?
Test setup:
create table demo_table
( some_id integer primary key
, some_name varchar2(30) not null unique
, some_type varchar2(10) not null );
insert all
into demo_table values (1, 'One', 'X' )
into demo_table values (2, 'Two', 'Y' )
into demo_table values (3, 'Three', 'Z' )
select * from dual;
Test:
(Edit: added dbms_output messages within loop)
declare
subtype demo_rectype is demo_table%rowtype;
type demo_rec is record
( details demo_rectype
, somecheck boolean );
type demo_tt is table of demo_rec index by pls_integer;
t_demo demo_tt;
cursor c_demo is select * from demo_table;
begin
for r in c_demo
loop
t_demo(c_demo%rowcount).details := r;
t_demo(c_demo%rowcount).somecheck := dbms_random.value < 0.5;
dbms_output.put_line
( t_demo(c_demo%rowcount).details.some_name || ': ' ||
case t_demo(c_demo%rowcount).somecheck
when true then 'TRUE'
when false then 'FALSE'
else 'NULL'
end );
end loop;
dbms_output.new_line();
dbms_output.put_line('Array t_demo contains ' || t_demo.count || ' items.');
end;
Output:
One: FALSE
Two: FALSE
Three: TRUE
Array t_demo contains 3 items.
Since the record type differs by 1 field from the table structure (i.e. the boolean), you can not assign the cursor row to the record type in 1 assignment statement. You need to do it for every table column individually:
for cursorrow in cursor(...)
loop
array(row i).col1 := cursorrow.col1;
array(row i).col2 := cursorrow.col2;
...
array(row i).coln := cursorrow.coln;
array(row i).boolean_variable := some_boolean_value;
end loop;
As mentioned in comments,you can create a record and do it. You can use the below procedure to achieve your requirement.
create or replace procedure proc_test
as
cursor cur_tab is
select a --have selected only 1 column..you can choose many
from test;
TYPE var_temp IS TABLE OF cur_tab%ROWTYPE INDEX BY PLS_INTEGER;
v_var var_temp;
/**You can add columns selected in you cursor here in your record****/
TYPE abc IS RECORD
(
id varchar2(100),
orig_name boolean
);
TYPE xx IS TABLE OF abc ;
-- initialization of record
v_xx xx := xx() ;
t boolean:=TRUE;
begin
open cur_tab;
fetch cur_tab bulk collect into v_var;
close cur_tab;
for i in 1..v_var.count
loop
v_xx.extend;
v_xx(i).id := v_var(i).a;
v_xx(i).orig_name := t;
dbms_output.put_line (v_xx(i).id ||'----'||sys.diutil.bool_to_int(v_xx(i).orig_name));
---OR
dbms_output.put_line (v_xx(i).id ||'----'||case when v_xx(i).orig_name = true then 'TRUE' ELSE 'FALSE' end );
end loop;
exception
when others then
null;
end;
Call:
execute proc_test;
CREATE OR REPLACE FUNCTION page(IN i_app name character varying, IN i_photo_id big int, IN i_page integer, IN i_member_id big int, OUT o_similar_page_name character varying, OUT o_similar_page_id big int, OUT o_similar_photo_id big int[])
DECLARE
v_limit INTEGER := 4;
v_offset INTEGER;
BEGIN
SET SEARCH_PATH = '';
v_start_time = DAYTIME();
i_app name = UPPER(i_app name);
IF i_app name <> 'DD' THEN
RAISE EXCEPTION 'Enter Valid Application Name';
END IF;
IF i_page = 1 THEN
v_offset := 0;
ELSE
v_offset := i_page * v_limit - v_limit;
END IF;
Please help me.
Answer
No.
Reason
Its not actually the purpose of SOLR. Functions has to be written in the DB level and the data that is retrieved out of query will be stored in SOLR for fast retrieval.
ALTERNATIVE SOLUTION
You can create the function and call it in the select statement to index the data into SOLR.
Note : Final results fetched out of functions can be stored in the SOLR.
Example:
CREATE FUNCTION CustomerLevel(p_creditLimit double) RETURNS VARCHAR(10)
DETERMINISTIC
BEGIN
DECLARE lvl varchar(10);
IF p_creditLimit > 50000 THEN
SET lvl = 'PLATINUM';
ELSEIF (p_creditLimit <= 50000 AND p_creditLimit >= 10000) THEN
SET lvl = 'GOLD';
ELSEIF p_creditLimit < 10000 THEN
SET lvl = 'SILVER';
END IF;
RETURN (lvl);
END
Query to used in SOLR for Indexing
SELECT CustomerLevel(123123123) as CustomerLevel from CustomerRating;