A generic procedure that can execute any procedure/function - plsql

input
Package name (IN)
procedure name (or function name) (IN)
A table indexed by integer, it will contain values that will be used to execute the procedure (IN/OUT).
E.g
let's assume that we want to execute the procedure below
utils.get_emp_num(emp_name IN VARCHAR
emp_last_name IN VARCHAR
emp_num OUT NUMBER
result OUT VARCHAR);
The procedure that we will create will have as inputs:
package_name = utils
procedure_name = get_emp_num
table = T[1] -> name
T[2] -> lastname
T[3] -> 0 (any value)
T[4] -> N (any value)
run_procedure(package_name,
procedure_name,
table)
The main procedure should return the same table that has been set in the input, but with the execution result of the procedure
table = T[1] -> name
T[2] -> lastname
T[3] -> 78734 (new value)
T[4] -> F (new value)
any thought ?

You can achieve it with EXECUTE IMMEDIATE. Basically, you build a SQL statement of the following form:
sql := 'BEGIN utils.get_emp_num(:1, :2, :3, :4); END;';
Then you execute it:
EXECUTE IMMEDIATE sql USING t(1), t(2), OUT t(3), OUT t(4);
Now here comes the tricky part: For each number of parameters and IN/OUT combinations you need a separate EXECUTE IMMEDIATE statement. And to figure out the number of parameters and their direction, you need to query the ALL_ARGUMENTS table first.
You might be able to simplify it by passing the whole table as a bind argument instead of a separate bind argument for each table element. But I haven't quite figured out how you would do that.
And the next thing you should consider: the elements of the table T your using will have a type: VARCHAR, NUMBER etc. So the current mixture where you have both numbers and strings won't work.
BTW: Why do you want such a dynamic call mechanism anyway?

Get from the all_arguments table the argument_name, data_type, in_out, and the position
Build the PLSQL block
DECLARE
loop over argument_name and create the declare section
argument_name data_type if in_out <> OUT then := VALUE OF THE INPUT otherwise NULL
BEGIN
--In the case of function create an additional argument
function_var:= package_name.procedure_name( loop over argument_name);
--use a table of any_data, declare it as global in the package
if function then
package_name.ad_table.EXTEND;
package_name.ad_table(package_name.ad_table.LAST):= function_var;
end if
--loop over argument_name IF IN_OUT <> IN
package_name.ad_table.EXTEND;
package_name.ad_table(package_name.ad_table.LAST):=
if data_type = VARCHAR2 then := ConvertVarchar2(argument_name)
else if NUMBER then ConvertNumber
else if DATE then ConvertDate
...
END;
The result is stored in the table.
To get value use Access* functions

Related

Procedure created with compilation errors. payment_count(l_p_count); *

Create a procedure named 'payment_count' which accepts one output parameter p_count with number as its data type. This procedure should print the count of successful payments (response_msg as 'Successful') with alias name as 'successful_payment_count'.
Hints: Procedure name : payment_count
Output parameter : p_count
Table used : payment_authorization
Function used : count
create or replace procedure payment_count(p_count output number)
as
begin
select count(response_msg='Successful') into successful_payment_count from payment_authorization;
return successful_payment_count;
end;
/
schema payment_authorization
Is this a task in an exam?
Sounds so... anyway:
You have to change keyword output to OUT, change the COUNT(response_msg='Successful') to COUNT(*) and add response_msg='Successful' to the where clause of the query.
create or replace procedure payment_count(p_count out number)
as
begin
select count(response_msg) as successful_payment_count
into p_count
from payment_authorization
where response_msg = 'Successful';
end;
/

mariadb user defined aggregate function

I am using mariadb 10.3.9, and have created a user defined aggregate function (UDAF) and placed in a common_schema. This schema contains my utility functions to be used by other schema/databases on the same server.
The issue is that when calling the UDAF while using any other schema, it always return NULL!
The following is to demonstrate the issue:
CREATE SCHEMA IF NOT EXISTS common_schema;
DELIMITER $$
DROP FUNCTION IF EXISTS common_schema.add_ints $$
CREATE FUNCTION common_schema.add_ints(int_1 INT, int_2 INT) RETURNS INT NO SQL
BEGIN
RETURN int_1 + int_2;
END $$
DROP FUNCTION IF EXISTS common_schema.sum_ints $$
CREATE AGGREGATE FUNCTION common_schema.sum_ints(int_val INT) RETURNS INT
BEGIN
DECLARE result INT DEFAULT 0;
DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN result;
LOOP FETCH GROUP NEXT ROW;
SET result = common_schema.add_ints(result, int_val);
END LOOP;
END $$
DELIMITER ;
Now, calling it this way, returns the result as expected:
USE common_schema;
SELECT common_schema.sum_ints(seq)
FROM (SELECT 1 seq UNION ALL SELECT 2) t;
-- result: 3
Calling it using any other schema, it returns NULL:
USE other_schema;
SELECT common_schema.sum_ints(seq)
FROM (SELECT 1 seq UNION ALL SELECT 2) t;
-- result: null
Am I missing something here? Is there any configuration that is missing?
Appreciate your help.
Reported as a Bug https://jira.mariadb.org/browse/MDEV-18100.
As a workaround, create the UDAF in every schema.

PL/SQL How to iterate and update from an query inside a loop

I don't know how to iterate and update from a query result inside the loop. Is it possible to loop again from the query inside my first loop? Here is my code:
CREATE OR REPLACE PROCEDURE "myTEST" (sp_type in char)
IS
CURSOR c1 IS
SELECT SP_ID FROM CI_SP
WHERE SP_TYPE_CD = sp_type;
sp_id char(10);
item_id_eq CI_SP_EQ.ITEM_ID_EQ%type;
BEGIN
FOR sp_rec in c1
LOOP
DBMS_OUTPUT.PUT_LINE(sp_rec.sp_id);
SELECT ITEM_ID_EQ INTO item_id_eq FROM CI_SP_EQ
WHERE SP_ID = sp_rec.sp_id;
DBMS_OUTPUT.PUT_LINE('item id eq :' || item_id_eq);
-- iterate here for each item_id_eq
-- execute update for each item_id_eq also
END LOOP;
END myTEST;
Instead of looping twice you could just do a join between CI_SP & CI_SP_EQ and get it done in one shot:
CREATE OR REPLACE PROCEDURE "myTEST"(sp_type IN CHAR) IS
BEGIN
FOR item IN (SELECT item_id_eq
FROM ci_sp_eq JOIN ci_sp USING (sp_id)
WHERE sp_type_cd = sp_type) LOOP
-- do your stuff.
NULL;
END LOOP;
END mytest;
I think you wouldn't even need a PL/SQL block, just a simple UPDATE will do, but I don't exactly know what you're trying to do.
Some other comments:
Don't create objects enclosed in "quotes", the object name is now case sensitive. In your case, the compilation will fail because you've created procedure name as "myTEST" and end it with mytest, which Oracle will treat it as "MYTEST" and you'll get compile error because of syntax check fail
Use VARCHAR2 instead of CHAR, CHAR will pad spaces if the input doesn't match the length specifier and will lead to further problems

How to place a plsql block inside a sequence

I am trying to create a user generated sequence. According to usual syntax of oracle sequence we can start with a number and increment a value.
Is there a method to write a plsql block (declare begin end) inside a sequence and generate my own sequnce.
example : ABC001
When i call the next val of sequence , the value should be ABC002
PLEASE CLEAR FIRST WHAT YOU EXACTLY WANT TO ASK.
If you are asking HOW TO DYNAMICALLY CREATE SEQUENCE USING PL/SQL, then check below.
Simplest way.
DECLARE
SQL_S VARCHAR2(100);
BEGIN
SQL_S := 'CREATE SEQUENCE SQN_NAME INCREMENT BY 1 START WITH 1';
EXECUTE IMMEDIATE SQL_S;
END;
/
If you want to dynamically create sequence with some DYNAMIC name as argument passed to procedure, then it will be like
CREATE OR REPLACE PROCEDURE DYNAMIC_SQN (ARG IN VARCHAR2) IS
SQL_S VARCHAR2(100);
PARAM1 VARCHAR2(20);
BEGIN
PARAM1 := 'SQN_NAME_' || ARG;
SQL_S := 'CREATE SEQUENCE ' || PARAM1 || ' INCREMENT BY 1 START WITH 1';
EXECUTE IMMEDIATE SQL_S;
END;
/
And if you simply want to insert in to any column, and create the PK using it in addition to some String, then
INSERT INTO TABLE_T VALUES('ABC'|| SEQUENCE_NAME.nextval, OTHER_VALUES);
It will still give you values like : ABC1, ABC2, .... ABC12, ABC13, .... ABC99, ABC100 and so on...
Considering the sample example you have given i m writing the following code
create your sequence, then
insert into seq values('ABC'||YOURSEQUENCENAME.nextval,YOUR_VALUE);

Convert stored proc to table to be able to use in select statement

I have to use existing stored procedure which returns REF CURSOR. I need to insert that resultset into a temporary table.
Spec of procedure is:
TYPE cur IS REF CURSOR;
PROCEDURE get(p_one NUMBER ,p_two OUT cur);
How can I insert the resultset of this procedure into a table.
I just re-read the title of your question. Do you actually need to be able to select from a procedure?
If so, this can be achieved by using pipelined functions.
The process for this is:
Create an object type to represent the record-type you require.
Create a nested table type of the object.
Create a pipelined function which returns the nest table.
You can then select from this function.
This example should get you on your way:
create or replace type to_test as object (
val1 varchar2(32),
val2 varchar2(32)
);
create or replace type tt_test as table of to_test;
create or replace function demo_pipe return tt_test pipelined
is
v_test to_test;
begin
for rec in (select * from user_tables) loop
v_test := to_test(rec.table_name, rec.tablespace_name);
pipe row (v_test);
end loop;
end;
/
select * from table(demo_pipe);

Resources