Oracle tuning / analyze tables - oracle

What are the means to schedule automatic "analyze tables". Is it possible to request an automatic "analyze tables" when a lot of data change througt insert and deletes? What are the means to parametrize the automatic analyze tables process, i.e. to set rules when it should be triggered.

What version of Oracle are you using? From 10.1 on, Oracle has shipped with an automatic job that gathers statistics every night on any object that has been changed substantially (based on an internal algorithm subject to change, but I believe the threshold is ~15%) since that last time statistics were gathered. Assuming you are using a currently supported release, statistics are gathered automatically by default unless you explicitly disable that job.
You can use the DBMS_STATS package to gather statistics on an object based on an event and you can use the DBMS_JOB (or DBMS_SCHEDULER) package to have that done asynchronously. So at the end of an ETL process, you can gather statistics
BEGIN
dbms_stats.gather_table_stats(
ownname => <<schema name>>,
tabname => <<table name>>
);
END;
You can have that run asynchronously
DECLARE
l_jobno INTEGER;
BEGIN
dbms_job.submit(
l_jobno,
'BEGIN dbms_stats.gather_table_stats( ''<<schema name>>'', ''<<table name>>'' ); END;',
sysdate + interval '1' minute
);
END;

"Is there a way to have this job being triggered when there is a substantial proportion of data changed"
In theory you could do an AFTER INSERT trigger on a table that automatically sets off DBMS_STATS.
But be careful what you wish for. If you are in the middle of a large ETL job, having just inserted a million rows into the table, you don't necessarily want to automatically immediately set off a DBMS_STATS job. Equally, when you are at your busiest on-line processing time (eg lunch) you don't want to slow it down by gathering stats at the same time.
You generally want these stats gathering jobs running at low-usage periods (nights, weekends). If you have a large batch run that loads into a lot of tables, I'd build the DBMS_STATS into the batch.

Related

How to turn off stats on Global Temporary Table and impact

Hello I am not very experienced on Oracle DB administration, I have several queries that started to work really slow and all involve with temp table (12c). I see several posts talking about disable stats on GTT (Global Temporary Table), however I didn't find much but just following setting to disable
exec dbms_scheduler.disable('SYS.GATHER_STATS_JOB');
my question is any specific way to turn off stats ONLY to GTT, and what is the negative impact on above command?
This disables statistics gathering on a table
begin
dbms_stats.delete_table_stats('TABLE_OWNER', 'TABLE_NAME');
dbms_stats.lock_table_stats('TABLE_OWNER', 'TABLE_NAME');
end;
/
However a better idea is to collect statistics on the table when it is filled with representative data set:
begin
dbms_stats.gather_table_stats('TABLE_OWNER', 'TABLE_NAME');
dbms_stats.lock_table_stats('TABLE_OWNER', 'TABLE_NAME');
end;
/
https://docs.oracle.com/cd/B28359_01/appdev.111/b28419/d_stats.htm#i1043993

How Oracle GATHER_SCHEMA_STATS works

We have one of our system that perform quite a bit of database activity in terms of INSERT/UPDATE/DELETE statements against various tables. Because of this the statistics became stale and this is reflected in overall performance.
We want to create a scheduled job that would periodically invoke DBMS_STATS.GATHER_SCHEMA_STATS. Because we don't want actual stats gathering itself to impact the system processing even more we are thinking to collect statistics quite frequent and use GATHER STALE option:
DBMS_STATS.GATHER_SCHEMA_STATS(OWNNAME => 'MY_SCHEMA', OPTIONS =>'GATHER STALE')
This executes almost instantly but running this statement below before and after stats gathering seems to bring back the same records with the same values:
SELECT * FROM user_tab_modifications WHERE inserts + updates + deletes > 0;
The very short time taking to execute and the fact that user_tab_modifications content stays the same makes me question if OPTIONS =>'GATHER STALE' actually does what we expect it should do. On the other hand if I run this again before and after statistics gathering I can see the tables reported as stale before re no longer reported as stale after:
DECLARE
stale dbms_stats.objecttab;
BEGIN
DBMS_STATS.GATHER_SCHEMA_STATS(ownname => 'MY_SCHEMA', OPTIONS =>'LIST STALE', objlist => stale);
FOR i in 1 .. stale.count
LOOP
dbms_output.put_line( stale(i).objName );
END LOOP;
END;
On another hand if lets say my_table is one of my tables being listed as part of the tables that part of the user_tab_modifications with inserts + updates + deletes > 0 and I run I can see my_table no longer being reported as having changes.
EXECUTE DBMS_STATS.GATHER_TABLE_STATS(ownname => 'MY_SCHEMA', tabname => 'MY_TABLE');
So my questions are:
Is my approach correct. Can I trust I am getting fresh stats just by running options => 'GATHER STALE' or I should manually collect stats for all tables that come back with a reasonable number of inserts, updates, deletes?
When user_tab_modifications would actually get reset; obviously GATHER STALE option does not seem to do it
We are using Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
Got the following info from Oracle docs.
You should enable monitoring if you use GATHER_DATABASE_STATS or GATHER_SCHEMA_STATS with the GATHER AUTO or GATHER STALE options.
This view USER_TAB_MODIFICATIONS is populated only for tables with the MONITORING attribute. It is intended for statistics collection over a long period of time. For performance reasons, the Oracle Database does not populate this view immediately when the actual modifications occur. Run the FLUSH_DATABASE_MONITORING_INFO procedure in the DBMS_STATS PL/SQL package to populate this view with the latest information. The ANALYZE_ANY system privilege is required to run this procedure.
Hope this helps you to identify which of your assumptions are incorrect and understand the correct usage of "GATHER STALE".

Detect if data in Oracle table has changed

I'm looking for a best performance solution to detect if data in an Oracle table has changed. This will be used to kick start a calculation that uses lots of data from the same tables. It would be too expensive to poll the data to track changes. The changes happen rarely.
I have analyzed the following solutions to the problem.
ORA_ROWSCN : Too slow, will do a full table scan.
Oracle Audit : Not possible to set up in my environment.
DBMS_ALERT : Writers are not able to signal.
Then I came up with the following simple idea. Add a trigger to the tables that increments a sequence on insert, update or delete. I know this will be materialized if rolled back but I can afford some false positives. My calculations service then polls the sequence current value to detect possible changes (= very cheap query)
CREATE OR REPLACE TRIGGER trg_change_tracker
before insert or update or delete on mytable
declare
dummy number;
begin
select seq_event_seqno.nextval into dummy from dual;
end;
How does this sound? Any pitfalls?
EDIT: Yes there is a major pitfall: When the writer holds back the commit, and the reader sees the new sequence value and query for changes before the writer has committed.
I can suggest you some addition to your trigger soulution:
When your polling see changes of the sequence, you can check opened transactions on interested table, if it exists, then skip current recalc.
UPD:
Also, you can save min SCN of opened transactions, and in case of frequently table changes you will not freeze
UPD2:
It is some heuristic improvement, not full problem solution. If you will skip recal every time when you see opened transaction, then you can freeze much time in case of frequently (and may be long) DML on table.
I mean, when seq is changed and the polling see opened transactions on your table, you can store min(start_scn) from v$transaction, and when the polling see opened transactions at next time you can compare current min(start_scn) with sotred min(start_scn), if current is greater then it is some chance that it is time to recalc.
You can use ( for particular XTABLE ):
SELECT * FROM dba_tab_modifications WHERE TABLE_NAME = 'XTABLE';
it will allow you ( among other data ) to see:
INSERTS UPDATES DELETES
43,708 1,845 0
.. there are issues ( bugs in some Oracle versions ), but if you will test thoroughly in your environment - you may find this useful.
.. as this question is pretty old - it would be nice to know how you implemented the change detection mechanism.

concurrent statistics gathering on Oracle 11g partiitioned table

I am developing a DWH on Oracle 11g. We have some big tables (250+ million rows), partitioned by value. Each partition is a assigned to a different feeding source, and every partition is independent from others, so they can be loaded and processed concurrently.
Data distribution is very uneven, we have partition with millions rows, and partitions with not more than a hundred rows, but I didn't choose the partitioning scheme, and by the way I can't change it.
Considered the data volume, we must assure that every partition has always up-to-date statistics, because if the subsequent elaborations don't have an optimal access to the data, they will last forever.
So for each concurrent ETL thread, we
Truncate the partition
Load data from staging area with
SELECT /*+ APPEND */ INTO big_table PARTITION(part1) FROM temp_table WHERE partition_colum = PART1
(this way we have direct path and we don't lock the whole table)
We gather statistics for the modified partition.
In the first stage of the project, we used the APPROX_GLOBAL_AND_PARTITION strategy and worked like a charm
dbms_stats.gather_table_stats(ownname=>myschema,
tabname=>big_table,
partname=>part1,
estimate_percent=>1,
granularity=>'APPROX_GLOBAL_AND_PARTITION',
CASCADE=>dbms_stats.auto_cascade,
degree=>dbms_stats.auto_degree)
But, we had the drawback that, when we loaded a small partition, the APPROX_GLOBAL part was dominant (still a lot faster than GLOBAL) , and for a small partition we had, e.g., 10 seconds of loading, and 20 minutes of statistics.
So we have been suggested to switch to the INCREMENTAL STATS feature of 11g, which means that you don't specify the partition you have modified, you leave all parameters in auto, and Oracle does it's magic, automatically understanding which partition(s) have been touched. And it actually works, we have really speeded up the small partition. After turning on the feature, the call became
dbms_stats.gather_table_stats(ownname=>myschema,
tabname=>big_table,
estimate_percent=>dbms_stats.auto_sample_size,
granularity=>'AUTO',
CASCADE=>dbms_stats.auto_cascade,
degree=>dbms_stats.auto_degree)
notice, that you don't pass the partition anymore, and you don't specify a sample percent.
But, we're having a drawback, maybe even worse that the previous one, and this is correlated with the high level of parallelism we have.
Let's say we have 2 big partition that starts at the same time, they will finish the load phase almost at the same time too.
The first thread ends the insert statement, commits, and launches the stats gathering. The stats procedure notices there are 2 partition modified (this is correct, one is full and the second is truncated, with a transaction in progress), updates correctly the stats for both the partitions.
Eventually the second partition ends, gather the stats, it see all partition already updated, and does nothing (this is NOT correct, because the second thread committed the data in the meanwhile).
The result is:
PARTITION NAME | LAST ANALYZED | NUM ROWS | BLOCKS | SAMPLE SIZE
-----------------------------------------------------------------------
PART1 | 04-MAR-2015 15:40:42 | 805731 | 20314 | 805731
PART2 | 04-MAR-2015 15:41:48 | 0 | 16234 | (null)
and the consequence is that I occasionally incur in not optimal plans (which mean killing the session, refresh manually the stats, manually launch the precess again).
I tried even putting an exclusive lock on the gathering, so no more than one thread can gather stats on the same table at once, but nothing changed.
IMHO this is an odd behaviour, because the stats procedure, the second time it is invoked, should check for the last commit on the second partition, and should see it's newer than the last stats gathering time. But seems it's not happening.
Am I doing something wrong? Is it an Oracle bug? How can I guarantee that all stats are always up-to-date with incremental stats feature turned on, and an high level of concurrency?
I managed to reach a decent compromise with this function.
PROCEDURE gather_tb_partiz(
p_tblname IN VARCHAR2,
p_partname IN VARCHAR2)
IS
v_stale all_tab_statistics.stale_stats%TYPE;
BEGIN
BEGIN
SELECT stale_stats
INTO v_stale
FROM user_tab_statistics
WHERE table_name = p_tblname
AND object_type = 'TABLE';
EXCEPTION
WHEN NO_DATA_FOUND THEN
v_stale := 'YES';
END;
IF v_stale = 'YES' THEN
dbms_stats.gather_table_stats(ownname=>myschema,
tabname=> p_tblname,
partname=>p_partname,
degree=>dbms_stats.auto_degree,
granularity=>'APPROX_GLOBAL AND PARTITION') ;
ELSE
dbms_stats.gather_table_stats(ownname=>myschema,
tabname=>p_tblname,
partname=>p_partname,
degree=>dbms_stats.auto_degree,
granularity=>'PARTITION') ;
END IF;
END gather_tb_partiz;
At the end of each ETL, if the number of added/deleted/modified rows is low enough not to mark the table as stale (10% by default, can be tuned with STALE_PERCENT parameter), I collect only partition statistics; otherwise i collect global and partition statistics.
This keeps ETL of small partition fast, because no global partition must be regathered, and big partition safe, because any subsequent query will have fresh statistics and will likely use an optimal plan.
Incremental stats is anyway enabled, so whenever the global has to be recalculated, it is pretty fast because aggregates partition level statistics and does not perform a full scan.
I am not sure if, with incremental enabled, "APPROX_GLOBAL AND PARTITION" and "GLOBAL AND PARTITION" do differ in something, because both incremental and approx do basically the same thing: aggregate stats and histograms without doing a full scan.
Have you tried to have incremental statistics on, but still explicitly name a partition to analyze?
dbms_stats.gather_table_stats(ownname=>myschema,
tabname=>big_table,
partname=>part,
degree=>dbms_stats.auto_degree);
For your table, stale (yesterday's) global stats are not as harmful as completely invalid partition stats (0 rows). I can propose 2 a bit alternative approaches that we use:
Have a separate GLOBAL stats gathering executed by your ETL tool right after all partitions are loaded. If it's taking too long, play with estimate_percent as dbms_stats.auto_degree will likely to be more than 1%
Gather the global (as well as all other stale) stats in a separate database job run later during the day, after all data is loaded into DW.
The key point is that stale statistics which differ only slightly from fresh are almost just as good. If statistics show you 0 rows, they'll kill any query.
Considering what you are trying to achieve, you need to run stats on specific intervals of time for all Partitions and not at the end of the process that loads each partition. It could be challenging if this is a live table and has constant data loads happening round the clock, but since these are LARGE DW tables I really doubt that's the case. So the best bet would be to collect stats at the end of loading all partitions, this will ensure that the statistics is collected for partitions where data has change or statistics are missing and update the global statistics based on the partition level statistics and synopsis.
However to do so, you need to turn on incremental feature for the table (11gR1).
EXEC DBMS_STATS.SET_TABLE_PREFS('<Owner>','BIG_TABLE','INCREMENTAL','TRUE');
At the end of every load, gather table statistics using GATHER_TABLE_STATS command. You don't need to specify the partition name. Also, do not specify the granularity parameter.
EXEC DBMS_STATS.GATHER_TABLE_STATS('<Owner>','BIG_TABLE');
Kindly check if you have used DBMS_STATS to set table preference to gather incremental statistics.This oracle blog explains that statistics will be gathered after each row affected.
Incremental statistics maintenance needs to gather statistics on any partition that will change the global or table level statistics. For instance, the min or max value for a column could change after just one row is inserted or updated in the table
BEGIN
DBMS_STATS.SET_TABLE_PREFS(myschema,'BIG_TABLE','INCREMENTAL','TRUE');
END;
I'm a bit rusty about it, so first of all a question:
did you try serializing partition loading? If so, how long and how well does statistics run? Notice that since loading time is so much smaller than statistics gathering, i guess this could also act as a temporary workaround.
Append hint does affects redo size, meaning the transaction just traces something, thus statistics may not reckon new data:
http://oracle-base.com/articles/misc/append-hint.php
Thinking out loud: since the direct path insert does append rows at the end of the partition and eventually updates metadata at the end, the already running thread gathering statistics could have read non-updated (stale) data. Thus it may not be a bug, and locking threads would accomplish nothing.
You may test this behaviour temporarily switching your table/partition to LOGGING, for instance, and see how it works (slower, of course, but it's a test). Can you do it?
EDIT: incremental stats should work anyway, even disabling a parallel statistics gathering, since it reiles on the incremental values no matter how they were collected:
https://blogs.oracle.com/optimizer/entry/incremental_statistics_maintenance_what_statistics

Can I get a return value in some way from a dbms_scheduler job?

I have a webpage (generated via PL/SQL) that allows someone to toggle a remote device on or off. They are presented with a list of devices and they use checkboxes to select the ones to toggle. UTL_HTTP is used to communicate with the devices. Currently, the devices are toggled serially. Once all have been toggled, an email is sent to the user. Depending on how many devices are selected, doing this serially has the potential to take too long. So I'm looking at using DBMS_SCHEDULER to execute the toggling in parallel.
The problem is that the toggling process returns a status, either 'OK' or the reason it failed. I need that result to include in the email to the user. So, I need the 'main' procedure to create the SCHEDULER jobs and then wait for them to finish (and somehow get their statuses) before sending an email to the user.
Is this possible, short of having each job write it's status to a table which is polled by the 'main' process? I've read references to DBMS_PIPE for inter-process communication, but haven't found a good example (ie, one that makes sense to me) showing how to do it.
If there's a way to do it, I couldn't figure it out. I ended up having each job write it's status to a table. The main process knows how many individual jobs were created and polls the table to tell when all jobs are finished (or it timesout after a specified amount of time has passed, in case one of the jobs dies for some reason).
An Alternate Solution to Watching Parallel Processes Run Through DBMS_SCHEDULER Jobs
New Edit: An Abbreviated Discussion of the Core Problem
(Edit: 03/10/2014) I added this discussion after some helpful feedback from one of the posters watching this thread.
Opening Comments: Other discussion threads here mention the use of some output value from the function call itself. This is not natively possible with the existing DBMS_SCHEDULER features.
There are no OUT type parameter values or function outputs related to notifying a condition encountered by the procedure invoked. The most immediate problem at hand is: How do we run a series of related tasks via PL/SQL stored procedure in parallel? (i.e., without waiting for one another to finish before each start themselves.)
Whatever invokes the procedure tasks should not wait around for a status output. The response time is likely to be widely variable and whatever calls the procedure in this way would also hang. Associated processes would be waiting for the completion of the procedure or the return of a specified output.
A Suggested Approach: Other comments on this problem are on the right track. Have the custom output written to a table where it can be queried later when a response is ready. If you really want to make this a hands-off task, try putting a trigger on the output table. Every time a message of a specific value (representing a completed state of the request) is populated, invoke a procedure with Oracle's Mail package that sends out your notification email.
How to Track Your Invoked Jobs By Understanding DBMS_SCHEDULER Features
Using a scheduled job is a good way of initiating and watching a set of procedure calls that are not sequentially dependent on one another. I have been able to accomplish a similar approach using the original DBMS_JOB functionality of previous versions of the Oracle database.
A Case Study: Using a web-based application interface (Oracle Application Express) I had a project which allowed the user to initiate a resource-intensive series of database operations. Initiating the request was all that was needed.
The Actual Use Scenario: The user didn't need to wait around for its completion. The problem was that wiring the web request form directly to a call to this database package and its procedures also tied down control of the form and its session making the user "wait" for the procedure itself to complete.
Firing off a scheduled job that invoked this process separated the interaction with the web page from the wait for the actual process to complete. Scheduling a job task was almost instantaneous, so the wait between the submission of the request and return of control back to the web page also had a negligible wait.
Using Oracle DBMS_SCHEDULER: An Introduction to the Approach
The Current Problem and Solution Under Discussion: Use the native DBMS_SCHEDULER status views to monitor the progress of your process. There are many, but ALL_SCHEDULER_JOB_LOG is the simpler of the collection and is a good start to what we're trying to accomplish.
Use an easily identifiable name for each job... and also each job that may be related to one another.
Initiate an additional job to watch all the parallel tasks until the last one is completed. Alter this "watching" job to end once this condition has been met.
The Basic Syntax to Initiate a Database Job on Scheduler
The procedure DBMS_SCHEDULER.CREATE_JOB creates a job in a single call without using an existing program or schedule:
DBMS_SCHEDULER.CREATE_JOB (
job_name IN VARCHAR2,
job_type IN VARCHAR2,
job_action IN VARCHAR2,
number_of_arguments IN PLS_INTEGER DEFAULT 0,
start_date IN TIMESTAMP WITH TIME ZONE DEFAULT NULL,
repeat_interval IN VARCHAR2 DEFAULT NULL,
end_date IN TIMESTAMP WITH TIME ZONE DEFAULT NULL,
job_class IN VARCHAR2 DEFAULT 'DEFAULT_JOB_CLASS',
enabled IN BOOLEAN DEFAULT FALSE,
auto_drop IN BOOLEAN DEFAULT TRUE,
comments IN VARCHAR2 DEFAULT NULL);
These are the input parameters that you should pay close attention to:
job_name You can leave it to default, or you can help organize your job requests by using a consistent naming convention. JOB_NAME is a special parameter because it has a helper function called *GENERATE_JOB_NAME*, where you can specify a naming prefix to combine with the internal name assignment. This isn't absolutely necessary, but it helps.
DBMS_SCHEDULER.GENERATE_JOB_NAME
(prefix IN VARCHAR2 DEFAULT 'JOB$_') RETURN VARCHAR2;
An example call within the create_job definition:
job_name => dbms_scheduler.generate_job_name( prefix => 'MY_EXAMPLE_JOB_')
So in the example above, we could have a series of jobs with names like: MY_EXAMPLE_JOB_0001, MY_EXAMPLE_JOB_0002, MY_EXAMPLE_JOB_0003...
job_type This one is straight from the Oracle documentation. Most likely it will be the type: *plsql_block* (The value may also be case sensitive).
repeat_interval Do not set this value for your one-time parallel tasks. The job will identify itself as completed once the referenced stored procedure reaches completion, or errors out.
end_date Leave this null or unassigned as well. This value does not apply for a one-time execution of the procedure it is watching.
start_date Leave this null or unassigned as well. No value means to initiate the specified job as soon as the job is ENABLED.
enabled Defaulted to FALSE you will need to set this to TRUE as soon as you create the job, or when you're ready to initiate the process "thread".
auto_drop This is an important one. The rest of this method depends on the meta data of each job remaining in the DBMS_SCHEDULER's log tables even after they have hit an exception or reached completion. Set this to FALSE.
job_action This will vary depending on the number of parallel processes you initiate. First, you should initiate the first of your parallel processes... and also the related "Monitoring" process that will be active for a specific request. Job actions for a plsql_block type job looks something like this:
Example PL/SQL Block:
BEGIN my_procedure(a, b, c); END;
Creating a Job Monitoring Process
Part of the problem you encountered is that DBMS_SCHEDULER may watch a process of varying execution time, but it's not very good at letting you know when it's done or if it encountered an exception.
Your "watcher" process just needs to be another scheduled job that queries the ALL_SCHEDULER_JOB_LOG table for the procedures it's responsible for, and figures out if all of them have reached the desired closing status.
Assumption: For a given request, you will know the number of parallel processes (remotely initiated switching events) required to complete this type of request... all the processes do not have to start exactly at the same time, but the watcher will need to know it still has to wait, even if all the related processes it can see have matched its criteria as "completed".
The kinds of tasks your "watching" procedure will need to include:
WATCHING SQL Criteria Example:
WITH MONITOR_QUERY AS (
SELECT COUNT(LOG_ID) AS COMPLETED_PROCESS_COUNT
FROM ALL_SCHEDULER_JOB_LOG
WHERE JOB_NAME LIKE '001-REQUEST%')
SELECT CASE WHEN COMPLETED_PROCESS_COUNT = <TOTAL_PROCESSES>
THEN 'DONE' ELSE 'IN-PROGRESS' END as REQUEST_STATUS
FROM MONITOR_QUERY
Also note that when you call the job that runs the monitoring process, you may find it useful to generate a unique job name ahead of time before kicking off its repeating job (should be done only once per request or set of parallel jobs):
DECLARE
who_am_i VARCHAR2(65);
BEGIN
SELECT dbms_scheduler.generate_job_name
INTO who_am_i
FROM DUAL;
--
DBMS_SCHEDULER.CREATE_JOB (job_name => who_am_i,
job_type => 'plsql_block',
job_action => 'BEGIN my_monitoring_task(p_1, p_2, who_am_i); END',
repeat_interval => 300,
comments => 'Interval time units are defaulted to SECONDS';
...);
END;
This job is most effective if it is created at the same time or shortly after the first parallel job in the series is launched.
When The Monitored Request is Completed
When the selection criteria is met (i.e., all related processes are closed in some way) then it's time to fire off your notification and also to stop the watcher for this request.
Stopping the Monitoring Job
DBMS_SCHEDULER.STOP_JOB (
job_name IN VARCHAR2
force IN BOOLEAN DEFAULT FALSE);
job_name If you used a custom naming scheme for each job you initiate, you could also store this value as an input parameter into your watcher procedure call. The watcher would then know how to shut itself off when it's done. Remember, if you use the GENERATE_JOB_NAME function call, you are only specifying the prefix to the entire job_name used in the scheduler.
force Set this one to FALSE (The default) or leave it unspecified. It is better to let Oracle find a way to gracefully put your watcher job to a halt.
Closing Thoughts and Comments
If the outcome or completion of several of your procedures are related, an additional scheduled job can be repeated as a monitoring "heartbeat" to check if all the dependencies for a discrete process have been met.
A comment on clean-up: This design requires the *auto_drop* parameter set as FALSE. A daily or weekly process could also be scheduled to issue a *drop_job* command that will clean up the scheduler's logs of records related to completed and reported requests.
You can also see, by including the invoking *job_name* within the scheduled job itself, you can provide the procedure(s) contained within it the ability to turn itself off once the right conditions have been met.
Advanced Queues to the rescue.
Slave sessions (jobs), when they are ready, put their return values into an AQ (practically any data structure is allowed).
The coordinator session, which initiated the slaves, listens on the queue and gathers the return values.
Actually, AQs are the recommended way for intersession communication in Oracle anyway.
In Oracle 12c the column ALL_SCHEDULER_JOB_RUN_DETAILS.OUTPUT can be used to return values from a job.
For example, create a job and write output using DBMS_OUTPUT:
begin
dbms_scheduler.create_job(
job_name => 'TEST_JOB',
job_type => 'PLSQL_BLOCK',
job_action => q'[begin dbms_output.put_line('Test output'); end; ]',
enabled => true
);
end;
/
Now read the output:
select job_name, to_char(log_date, 'YYYY-MM-DD') log_date, output
from all_scheduler_job_run_details
where owner = user
and job_name = 'TEST_JOB'
order by log_date desc;
JOB_NAME LOG_DATE OUTPUT
-------- -------- -------
TEST_JOB 2017-12-26 Test output
If you are able to use oracle version 11, you might use the DBMS_PARALLEL_EXECUTE pl/sql package, which does what you want. If you cannot upgrade, then you can implement some c callouts from pl/sql which provide similar functionality.
If you decide to use dbms_pipe and you are using the RAC database option be aware that using DBMS_PIPE has its limitations for failover.

Resources