Parallelizing calls in PL/SQL - oracle

I have a package with a proc that will execute a number of other procedures, like so:
CREATE PACKAGE BODY pkg IS
CREATE PROCEDURE do
IS
BEGIN
other_pkg.other_proc;
other_pkg2.other_proc2;
other_pkg3.other_proc3;
END;
END;
Is there any way to have the procedures execute in parallel rather than serially?
EDIT:
Is this the proper way to use DBMS_SCHEDULER in this instance:
CREATE PACKAGE BODY pkg IS
CREATE PROCEDURE do
IS
BEGIN
DBMS_SCHEDULER.CREATE_JOB('job_other_pkg.other_proc', 'STORED_PROCEDURE', 'other_pkg.other_proc;');
DBMS_SCHEDULER.RUN_JOB('job_other_pkg.other_proc', FALSE);
-- ...
END;
END;

You can use the dbms_job (or dbms_scheduler) package to submit jobs that will run in parallel. If you are using dbms_job, submitting the jobs will be part of the transaction so the jobs will start once the transaction completes.
CREATE PACKAGE BODY pkg IS
CREATE PROCEDURE do
IS
l_jobno pls_integer;
BEGIN
dbms_job.submit(l_jobno, 'begin other_pkg.other_proc; end;' );
dbms_job.submit(l_jobno, 'begin other_pkg2.other_proc2; end;' );
dbms_job.submit(l_jobno, 'begin other_pkg3.other_proc3; end;' );
END;
END;
If you are using dbms_scheduler, creating a new job is not transactional (i.e. there would be implicit commits each time you created a new job) which may cause problems with transactional integrity if there is other work being done in the transaction where this procedure is called. On the other hand, if you are using dbms_scheduler, it may be easier to create the jobs in advance and simply run them from the procedure (or to use dbms_scheduler to create a chain that runs the job in response to some other action or event such as putting a message on a queue).
Of course, with either solution, you'd need to then build the infrastructure to monitor the progress of these three jobs assuming that you care when and whether they succeed (and whether they generate errors).
If you are going to use DBMS_SCHEDULER
There is no need to use dynamic SQL. You can ditch the EXECUTE IMMEDIATE and just call the DBMS_SCHEDULER package's procedures directly just like you would any other procedure.
When you call RUN_JOB, you need to pass in a second parameter. The use_current_session parameter controls whether the job runs in the current session (and blocks) or whether it runs in a separate session (in which case the current session can continue on and do other things). Since you want to run multiple jobs in parallel, you would need to pass in a value of false.
Although it is not required, it would be more conventional to create the jobs once (with auto_drop set to false) and then just run them from your procedure.
So you would probably want to create the jobs outside the package and then your procedure would just become
CREATE PACKAGE BODY pkg IS
CREATE PROCEDURE do
IS
BEGIN
DBMS_SCHEDULER.RUN_JOB('job_other_pkg.other_proc', false);
DBMS_SCHEDULER.RUN_JOB('job_other_pkg2.other_proc2', false);
DBMS_SCHEDULER.RUN_JOB('job_other_pkg3.other_proc3', false);
END;
END;

Another solution is to hack Oracle's SQL parallelism mechanism. See answer to How to execute a stored procedure in a different session in same time in pl/sql .
It uses William Robertson's great solution Parallel PL/SQL launcher.
(tested with Oracle 10g)

Related

Calling another PL/SQL procedure within a procedure

I'm new to PL/SQL & would greatly appreciate help in this. I've created a procedure to copy contracts. Now I want to call another procedure from within this procedure which shall copy all the programs related to the contract I'm copying. One contract can have multiple programs.
You cal call another procedure in another package by using PackageName.ProcedureName(vcParameters => 'InputParameter1'); If the procedure is in the same package you could do it without the PackageName, so just ProcedureName(vcParameters => 'InputParameter1');
You call a procedure by simply putting its name and parameters in your code, e.g.
begin
dbms_output.put_line('Demo');
end;
or within a procedure,
create or replace procedure demo
as
begin
dbms_output.put_line('Demo');
end;
I have used dbms_output.put_line as an example of a procedure, but obviously any other procedure would be called the same way:
begin
foo;
bar(1);
demo(true, 'Bananas', date '2018-01-01');
end;
For some reason, many beginners are tempted to add exec before the procedure call. I don't know where that comes from because PL/SQL has no such keyword. Possibly they are thinking of the SQL*Plus execute command, which can be abbreviated to exec. However, SQL*Plus is a separate command line utility with its own commands that have nothing to do with the PL/SQL language.

Calling an Oracle procedure from Java - what happens when System.exit() is called?

Here is code for an Oracle procedure to be called from Java:
CallableStatement st = connection.prepareCall("{call PROCEDURE_0(?,?,?)}");
st.setInt(1,xyz1);
st.setString(2,xyz2);
st.setInt(3,int_variable);
st.registerOutParameter(3,Types.INTEGER);
try{
st.execute();
}
catch(Exception e){
}
If a user calls System.exit() on this Java JVM - what happens to the Oracle procedure that was called? Is it guaranteed to keep running in Oracle? Or perhaps, in order to guarantee this, I should submit it to the Oracle job scheduler instead? So far, my experience has been that sometimes the procedure keeps running even if the JVM terminates, and sometimes it does not. Does this have anything to do with 'registering an out parameter'? Will the code reach the catch block if System.exit is called?
Does anyone have experience with this?
This code which I stole from Mike McAllister which he also stole from an Oracle forum solves the problem I was mentioning in this question. It works, and you can pass dynamic variables as parameters, not just hard-coded values.
this SO question also addresses the problem:
Passing arguments to oracle stored procedure through scheduler job
-- create a stored procedure with two arguments
create or replace procedure myproc (arg1 in varchar2, arg2 in varchar2)
is BEGIN null; END;
/
-- create a program with two arguments and define both
begin
dbms_scheduler.create_program
(
program_name=>'myprog',
program_action=>'myproc',
program_type=>'STORED_PROCEDURE',
number_of_arguments=>2, enabled=>FALSE
) ;
dbms_scheduler.DEFINE_PROGRAM_ARGUMENT(
program_name=>'myprog',
argument_position=>1,
argument_type=>'VARCHAR2',
DEFAULT_VALUE=>'13');
dbms_scheduler.DEFINE_PROGRAM_ARGUMENT(
program_name=>'myprog',
argument_position=>2,
argument_type=>'VARCHAR2');
dbms_scheduler.enable('myprog');
end;
/
-- create a job pointing to a program and set both argument values
begin
dbms_scheduler.create_job('myjob',program_name=>'myprog');
dbms_scheduler.set_job_argument_value('myjob',1,'first arg');
dbms_scheduler.set_job_argument_value('myjob',2,'second arg');
dbms_scheduler.enable('myjob');
end;
/

compiling invalid oracle procedures

I have a wrapper procedure(proc_main) that calls some procedures within.
create or replace Procedure proc_main
as
begin
proc_child1;
proc_child2;
proc_child3;
proc_compile_invalids; -- This invokes "alter procedure <procedure_name> compile" statement for all the invalids.
end;
/
proc_child procedures apply some processing logic that involves some steps to rename the tables within.
This invalidates the procedures which is the reason why I have the proc_compile_invalids procedure to set them to a valid state again.
My problem is: when I execute the proc_main procedure, it invalidates the main procedure along with the inner child ones.
Hence, When the proc_compile_invalids is called as a last step, it hangs as it is trying to recompile the main calling procedure.
Obviously, it is not an issue if i remove the last step and execute it separately.
I know I could separate them out as 2 different calls by commenting the compile proc and executing it as a stand alone.
And i also am aware it is a cosmetic step as oracle would try to compile a procedure before executing the next time. So, the invalids become valid anyway.
But, at the end of the execution for that day, they all are in an invalid state and I get questioned by the powers be if it can be avoided !
So, just wanted to know if I can avoid separating the calls and still retain it as a last step in the main procedure.
Any thoughts/pointers much appreciated.
You can use dynamic SQL to break the dependency:
CREATE OR REPLACE PROCEDURE proc_main AS
BEGIN
EXECUTE IMMEDIATE 'BEGIN proc_child1; END;';
EXECUTE IMMEDIATE 'BEGIN proc_child2; END;';
EXECUTE IMMEDIATE 'BEGIN proc_child3; END;';
proc_compile_invalids; -- This invokes
-- "alter procedure <procedure_name> compile"
-- statement for all the invalids.
END;
Oracle 11g onward
You can use compile_schema procedure of dbms_utility package instead of proc_compile_ivalids in your main procedure to recompile all invalid procedures, functions, packages, and triggers in the specified schema
create or replace Procedure proc_main
as
begin
Proc_child1;
proc_child2;
proc_child3;
dbms_utility.compile_schema(schema, false);
end;

Oracle trigger to queue/dequeue doesn't fire

Oracle 11g.
I have a trigger which calls a procedure. That procedure performs queue and dequeue.
When I run the procedure in sqldeveloper, it runs fine.
If I update the tables which the trigger fires off, the procedure runs fine.
If I go into the application in question and run a process which causes the trigger to fire, the procedure doesn't work. Specifically the queue and dequeue don't work because if I remove the queue/dequeue code, and instead do an insert on a table, the procedure runs fine.
So I'm thinking that maybe there's a permissions problem causing the trigger unable to execute on dbms_aq. Does anyone know how to resolve this? Is there a user account that runs the triggers which needs to be given privileges?
I don't think the problem is with the procedure because it runs fine. I'm also not passing any dynamic data from the trigger so the way I run the procedure under sqldeveloper is exactly the same way the trigger is calling the procedure.
Note: sanitized code
create or replace
TRIGGER mytrig
AFTER INSERT ON INVENTORY_TRANSACTION
FOR EACH ROW
WHEN ((NEW.CLIENT = 'abcwidgets') AND (NEW.CODE = 'Receipt'))
BEGIN
dschema.mypack.queue_receipt('abcwidgets','999','bbb','1','88');
EXCEPTION
WHEN OTHERS THEN NULL;
END;
PROCEDURE QUEUE_RECEIPT(
CLIENTID IN VARCHAR2
, RECEIPTID IN VARCHAR2
, SITEID IN VARCHAR2
, LINEID IN VARCHAR2
, UPDATE_QTY IN VARCHAR2
) AS
ctxHandle dbms_xmlgen.ctxHandle;
l_xml xmltype;
queueopts DBMS_AQ.ENQUEUE_OPTIONS_T;
msgprops DBMS_AQ.MESSAGE_PROPERTIES_T;
msgid RAW(16);
BEGIN
ctxHandle := dbms_xmlgen.newContext('SELECT line_id,
user_data_4
FROM inventory
');
l_xml := xmltype(dbms_xmlgen.getxml(ctxHandle));
DBMS_AQ.ENQUEUE ('DSCHEMA.ABCWIDGETS_QUEUE',
queueopts,
msgprops,
l_xml,
msgid);
END QUEUE_RECEIPT;
You probably need to fully qualify the QUEUE NAME. The queue name namespace is determined at runtime, and with no public synonym on queues, it will look in the logged in user namespace first, regardless of the procedure owner. If I had to guess, when you login to run it in sqldeveloper, it is probably as the user that owns the procedure you are calling.
The problem was explained to me by a dba which was a bit over my head. There was some variable declarations in a package I was using that were declared outside the scope of a body. I moved these back into the package bodies and this caused them to start working.

Oracle PL/SQL: a scheduled procedure, leading to firing of a trigger?

Okay, I'm new to Oracle PL/SQL and I've stumbled across a problem that I cannot figure out.
I have a procedure that leads to transferring data from one table to another and a trigger that activates on the insertion in the second table. I scheduled that procedure to run every minute (for testing - would be daily once I've figured it out), using the DBMS_JOB.SUBMIT - the scheduled part works perfectly, however after the completion of the procedure the trigger is not fired. I tried with before and after insert clauses, but it is still not working. If I call the procedure directly it works and it does fire the trigger just fine. So... I'm already wondering whether the scheduled procedure can fire the trigger at all?!
This is the schedule's code:
DECLARE
VJOBN BINARY_INTEGER;
BEGIN
DBMS_JOB.SUBMIT(
JOB => VJOBN,
INTERVAL => 'SYSDATE + 1/2880',
WHAT => 'BEGIN my_procedure(); END;'
);
END;
create or replace TRIGGER TO_PRJ
AFTER INSERT ON PROJECTS
FOR EACH ROW
BEGIN
IF INSERTING
THEN DBMS_OUTPUT.PUT_LINE('INSERTED PROJECT WITH ID: '||:NEW.PROJECT_ID||')
END IF;
END;​
Table PROJECTS has ID number, name varchar2, and some other that are not important.
The procedure transfers the ID and the name from orders to projects.
P.S. I'm using http://apex.oracle.com and when I get the timestamp from it the time is actually 6 hours behind me - not sure if it can be of any significance...
DBMS_OUTPUT and DBMS_JOB do not work the way you are trying to use them. The scheduled job is probably running, the trigger is firing - but since DBMS_OUTPUTneeds to be activated in the session that executes the DBMS_OUTPUT commands (i.e. the internal session used by DBMS_JOB) you will never see any output.
DBMS_OUTPUT's output is not visible across session, so the session that issues the DBMS_JOB.submit command will NOT receive the output, even if DBMS_OUTPUT is activated for that session.
Try using scheduler, it's much better then jobs. And bring there code of trigger and tables, it may help

Resources