Oracle text scheduling sync on single index - oracle

I need to sync an oracle text index. But job fails to create:
declare
v_job_id number(19,0);
begin
dbms_job.submit(
JOB => v_job_id,
WHAT => 'alter index NAME_IDX rebuild parameters (''sync'');',
NEXT_DATE => SYSDATE + (1/24),
INTERVAL => 'SYSDATE + (1/24) + 7'
);
end;
/
Or to run:
declare
v_job_id number(19,0);
begin
dbms_job.submit(
JOB => v_job_id,
WHAT => 'CTX_DDL(''NAME_IDX'');',
NEXT_DATE => SYSDATE + (1/24),
INTERVAL => 'SYSDATE + (1/24) + 7'
);
end;
/
But if I run any of those works:
alter index NAME_IDX rebuild parameters ('sync');
call CTX_DDL('NAME_IDX');
Any idea of the correct syntax?
Thank you.
PD: Ive been searching, but the only answer I found doesnt fit my requirements. I also apologize for my english.

You can run an anonymous block, CALL is not in PL/SQL, ALTER INDEX is DDL, and you need to specify which procedure in CTX_DDL you want to run:
WHAT => 'BEGIN EXECUTE IMMEDIATE ''alter index NAME_IDX rebuild parameters (''''sync'''')''; CTX_DDL.sync_index(''NAME_IDX''); END',
However, personally I prefer to encapsulate it in a procedure (or, even better, a package) and call the procedure from the job:
CREATE PROCEDURE rebuild_name_idx IS
BEGIN
EXECUTE IMMEDIATE 'alter index NAME_IDX rebuild parameters (''sync'')';
CTX_DDL.sync_index('NAME_IDX');
END;
/
declare
v_job_id number(19,0);
begin
dbms_job.submit(
JOB => v_job_id,
WHAT => 'rebuild_name_idx;',
NEXT_DATE => SYSDATE + (1/24),
INTERVAL => 'SYSDATE + (1/24) + 7'
);
end;
/
Also, I'm pretty sure you don't actually need to rebuild the index - you only need to call CTX_DDL.sync_index to refresh it from any DML on the table.

Related

How to call multiple procedures in parallel in Oracle 12c?

I have a procedure as follows,
CREATE OR REPLACE PROCEDURE engineering_all ( idx IN NUMBER )
IS
tempstmt VARCHAR2(2000);
BEGIN
create_table_like( 'results_temp', 'results', 1);
tempstmt := 'ALTER TABLE results_temp CACHE';
EXECUTE IMMEDIATE tempstmt;
engineering('CONSERVATIVE', idx);
engineering('INTERMEDIATE', idx);
engineering('AGGRESSIVE', idx);
END;
/
Three calls to procedure engineering are independent of each other, so I want to parallelize this. I came across multiple ways like DBMS_PARALLEL_EXECUTE, DBMS_JOB, DBMS_SCHEDULER but not able to figure out which one is the most effective for my time-optimization objective.
Please help me out to figure out which one to choose and how can I implement that?
I suggest to use DBMS_PARALLEL_EXECUTE. The main session waits till child parallel sessions are finished. There are convenient views with the statistics and results - user_parallel_execute_chunks and user_parallel_execute_tasks. We use it in our project, find it quite convenient.
One point is that this package requires chunking that can be done only by rowid or by numbers. So first of all you have to create the procedure wich accepts numbers. Here's the one for your case:
create or replace procedure engineering_parallel(
iLaunchType number,
idx number
)
is
begin
if iLaunchType = 1 then
engineering('CONSERVATIVE',idx);
elsif iLaunchType = 2 then
engineering('INTERMEDIATE',idx);
elsif iLaunchType = 3 then
engineering('AGGRESSIVE',idx);
end if;
end;
And here you will find an example of launching your case in anonymous pl/sql block, you can easily convert it into engineering_all procedure:
declare
-- idx parameter
idx number := 0;
-- unique parallel task name
sTaskName varchar2(32767) := 'ENGINEERING-'||to_char(sysdate,'yyyy-mm-dd-hh24-mi-ss');
-- this is where you store the query to split into chunks
sChunkSQL varchar2(32767) := 'select level start_id, '||idx||' end_id'||chr(10)||
'from dual connect by level <= 3';
-- this is the procedure call
sParallelSQL varchar2(32767) := 'begin engineering_parallel(:start_id,:end_id); end;';
-- parallel degree
iParalleDegree number := 3;
begin
-- create a task
DBMS_PARALLEL_EXECUTE.create_task(task_name => sTaskName);
-- chunking
DBMS_PARALLEL_EXECUTE.create_chunks_by_sql(
task_name => sTaskName,
sql_stmt => sChunkSQL,
by_rowid => FALSE
);
-- launch. current session waits till all child parallel sessions are finished
DBMS_PARALLEL_EXECUTE.run_task(
task_name => sTaskName,
sql_stmt => sParallelSQL,
language_flag => DBMS_SQL.NATIVE,
parallel_level => iParalleDegree
);
dbms_output.put_line(
'Job is finished.'||
'Check user_parallel_execute_chunks, user_parallel_execute_tasks for the task '||
sTaskName
);
end;
Last point to consider - check if your version includes the fix of Bug 18966843:
DBMS_PARALLEL_EXECUTE PERFORMANCE DELAY AFTER UPGRADE TO 11.2.0.4
We faced it in 12.1, but there are patches to fix it.
If it's not fixed then you have a chance that the parallel degree at the end will be less than requested (down to 1).
I never used the first option you mentioned, but - choosing between DBMS_JOB and DBMS_SCHEDULER, DBMS_JOB is simpler so I'd choose that. In its simplest way, procedure might look like this:
CREATE OR REPLACE PROCEDURE engineering_all (idx IN NUMBER)
IS
l_job NUMBER;
tempstmt VARCHAR2 (2000);
BEGIN
create_table_like ('results_temp', 'results', 1);
tempstmt := 'ALTER TABLE results_temp CACHE';
EXECUTE IMMEDIATE tempstmt;
DBMS_JOB.submit (
job => l_job,
what => 'begin engineering(''CONSERVATIVE'', ' || idx || '); end;',
next_date => SYSDATE,
interval => NULL);
DBMS_JOB.submit (
job => l_job,
what => 'begin engineering(''INTERMEDIATE'', ' || idx || '); end;',
next_date => SYSDATE,
interval => NULL);
DBMS_JOB.submit (
job => l_job,
what => 'begin engineering(''AGGRESSIVE'', ' || idx || '); end;',
next_date => SYSDATE,
interval => NULL);
COMMIT;
END;
/

Infinite job execution locking table

Here is the query:
INSERT INTO resumo
(tipo, id_tipo, qtde)
SELECT tipo, id_tipo, COUNT (*) qtde
FROM tabela
WHERE id_tipo > 400
GROUP BY tipo, id_tipo;
COMMIT;
If I run it manually, it took like 30 seconds to execute.
If I put the same query into a package and create a job to run it, it just keeps running forever, locking the table resumo. Any ideas why this happens??
It's oracle 11g.
EDIT
Package Spec:
CREATE OR REPLACE PACKAGE PKG_TESTE AS
procedure geraTabela;
End PKG_TESTE;
/
Package Body:
CREATE OR REPLACE PACKAGE body PKG_TESTE AS
procedure geraTabela is
begin
INSERT INTO resumo
(tipo, id_tipo, qtde)
SELECT tipo, id_tipo, COUNT (*) qtde
FROM tabela
WHERE id_tipo > 400
GROUP BY tipo, id_tipo;
COMMIT;
end;
end PKG_TESTE;
/
Job:
DECLARE
X NUMBER;
BEGIN
SYS.DBMS_JOB.SUBMIT
( job => X
,what => 'PKG_TESTE.geraTabela;'
,next_date => to_date('18/05/2016 11:00:00','dd/mm/yyyy hh24:mi:ss')
,interval => 'SYSDATE+60/1440 '
,no_parse => FALSE
);
COMMIT;
END;
/

oracle dbms_scheduler to run multiple procedures in parallel

I've trying to figure out oracle's DBMS_SCHEDULER (Oracle 11g) and need help setting up the following:
I have a procedure that calls a list of other procedures like this:
CREATE OR REPLACE
PROCEDURE RUN_JOBS AS
BEGIN
MYUSER.MYPROCEDURE1();
MYUSER.MYPROCEDURE2();
MYUSER.MYPROCEDURE3();
MYUSER.MYPROCEDURE4();
MYUSER.MYPROCEDURE5();
END;
/
I would like to use DBMS_SCHEDULER to run MYPROCEDURE3(), MYPROCEDURE4(), MYPROCEDURE5()
in parallel after the completion of MYPROCEDURE2().
Can someone show me an example on how to set this up?
You can refer to Chains under the DBMS_SCHEDULER package: http://docs.oracle.com/cd/B28359_01/server.111/b28310/scheduse009.htm
You can also achieve the same by going through Oracle Enterprise Manager, but I can't find any links to documentation right now.
You can do that using DBMS_SCHEDULER.
CREATE OR REPLACE PROCEDURE RUN_JOBS
AS
v_JobNum NUMBER := 1;
BEGIN
BEGIN
DBMS_JOB.SUBMIT(v_JobNum,'MYUSER.MYPROCEDURE1;',sysdate,'sysdate +1');
DBMS_JOB.SUBMIT(v_JobNum,'MYUSER.MYPROCEDURE2;',sysdate,'sysdate +1');
DBMS_JOB.SUBMIT(v_JobNum,'MYUSER.MYPROCEDURE3;',sysdate,'sysdate +1');
DBMS_JOB.SUBMIT(v_JobNum,'MYUSER.MYPROCEDURE4;',sysdate,'sysdate +1');
COMMIT;
END;
END RUN_JOBS;
/
This will submit the job and run them immediately.
create three different jobs for each procedure and schedule them at same time.
Here is my custom approach to parallellize work into N separate jobs retaining dbms_scheduler's logging and backpressure support. Date intervals are spread by mod N.
create table message_fixup_log (
source_date date not null,
started_at timestamp(6) not null,
finished_at timestamp(6),
fixed_message_count number(10)
);
alter table message_fixup_log add duration as (finished_at - started_at);
create unique index ix_message_fixup_log_date on message_fixup_log(source_date desc);
create or replace procedure message_fixup(jobNumber number, jobCount number, jobName varchar default null)
is
minSince date;
maxSince date;
since date;
msgUpdatedCount number;
begin
-- choose interval
select trunc(min(ts)) into minSince from message_part;
select trunc(max(ts))+1 into maxSince from message_part;
begin
select max(source_date) + jobCount into since from message_fixup_log
where finished_at is not null
and mod(source_date - minSince, jobCount) = jobNumber
and source_date >= minSince;
exception when no_data_found then null;
end;
if (since is null) then
since := minSince + jobNumber;
end if;
if (since >= maxSince) then
if (jobName is not null) then
dbms_scheduler.set_attribute(jobName, 'end_date', systimestamp + interval '1' second);
end if;
return;
end if;
insert into message_fixup_log(source_date, started_at) values(since, systimestamp);
-- perform some actual work for chosen interval
msgUpdatedCount := sql%rowcount;
update message_fixup_log
set fixed_message_count = msgUpdatedCount, finished_at = systimestamp
where source_date = since;
end;
-- manual test
--call message_fixup(0, 1);
declare
jobName varchar2(256);
jobCount number default 8;
begin
for jobNumber in 0..(jobCount-1) loop
jobName := 'message_fixup_job' || jobNumber;
begin
dbms_scheduler.drop_job(jobName, true);
exception
when others then null;
end;
dbms_scheduler.create_job(
job_name => jobName,
job_type => 'stored_procedure',
job_action => 'message_fixup',
enabled => false,
start_date => systimestamp,
repeat_interval => 'freq = minutely; interval = 1',
number_of_arguments => 3
);
dbms_scheduler.set_attribute(jobName, 'logging_level', dbms_scheduler.logging_full);
dbms_scheduler.set_job_anydata_value(jobName, 1, ANYDATA.ConvertNumber(jobNumber));
dbms_scheduler.set_job_anydata_value(jobName, 2, ANYDATA.ConvertNumber(jobCount));
dbms_scheduler.set_job_argument_value(jobName, 3, jobName);
dbms_scheduler.enable(jobName);
end loop;
end;

Can a JOB be created dynamically inside a trigger?

The execution of this trigger fails (it compiles but once I do the specified insert -> error)
create or replace
TRIGGER AFT_INSERT_TMP_TBL
AFTER INSERT ON TMP_TBL
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
DECLARE
V_SQL VARCHAR2(1000);
A_NAME VARCHAR2(100);
BEGIN
A_NAME:='ANY_NAME';
V_SQL:='BEGIN
DBMS_SCHEDULER.CREATE_JOB (
job_name => '''||A_NAME||''',
job_type => ''PLSQL_BLOCK'',
job_action => ''BEGIN DBMS_OUTPUT.PUT_LINE('||A_NAME||'); END;'',
start_date => TIMESTAMP''2011-12-4 10:30:00'',
repeat_interval => ''FREQ=MINUTELY;INTERVAL=2'',
auto_drop => FALSE,
comments => '''||A_NAME||''');
END;';
DBMS_OUTPUT.PUT_LINE('SCHEDULER :'||V_SQL);
EXECUTE IMMEDIATE V_SQL;
END AFT_INSERT_TMP_TBL;
-----------------------
Printed SCHEDULER creation code is totally valid.
I am getting a ORA-04092 'cannot in a trigger... A trigger attempted to commit or rollback. Rewrite the trigger so it doesn't commit or rollback'.
Is this a 'commit'? So a JOB cannot be created inside of a trigger?
I know I've used triggers with inserts into different tables, and that is also a "commit"
and Oracle didn't complaint.
Calling DBMS_SCHEDULER.CREATE_JOB implicitly commits so you cannot create a DBMS_SCHEDULER job in a trigger. This is one of the situations that still call for using the old DBMS_JOB package since DBMS_JOB.SUBMIT does not implicitly commit.
This trigger should create the job you want using the DBMS_JOB package rather than DBMS_SCHEDULER.
create or replace
TRIGGER AFT_INSERT_TMP_TBL
AFTER INSERT ON TMP_TBL
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
DECLARE
V_SQL VARCHAR2(1000);
A_NAME VARCHAR2(100);
l_jobno NUMBER;
BEGIN
A_NAME:='ANY_NAME';
dbms_job.submit( l_jobno,
'BEGIN dbms_output.put_line( ''' || a_name || ''' ); END;',
sysdate + interval '2' minute,
'sysdate + interval ''2'' minute' );
DBMS_OUTPUT.PUT_LINE('Job Number:'||l_jobno);
END AFT_INSERT_TMP_TBL;
You can also consider autonomous transaction
https://community.oracle.com/thread/2399412?start=0&tstart=0
Yes this is a commit. Oracle is updating a system table, job$ I think, when you create a scheduler or a job etc which means an automatic commit.
Why don't you just insert the information you need into another (driver) table and have a cron job or a dbms_job to create your scheduler jobs?

Oracle scheduled job fails

I am using Oracle 10g and using following script to create the job
CREATE OR REPLACE PROCEDURE archtemp AS
BEGIN
UPDATE ARCH_TEMP SET ARCH_DATE = SYSDATE;
COMMIT;
END archtemp;
VAR jobno NUMBER;
BEGIN
DBMS_JOB.SUBMIT(:jobno, 'archtemp;', SYSDATE, 'sysdate + 1/1440');
COMMIT;
END;
The job never executes automatically (though it runs manually) with following error in alert_sid.log
ORA-12012: error on auto execute of job 26
ORA-01422: exact fetch returns more than requested number of rows
ORA-06512: at line 8
I am unable to link the ORA-01422 error with any of my code. I'm not doing any fetch here.
Assuming this is a script for SQL*Plus, there are two / misssing, so it does nothing at all:
CREATE OR REPLACE PROCEDURE archtemp AS
BEGIN
UPDATE ARCH_TEMP SET ARCH_DATE = SYSDATE;
COMMIT;
END archtemp;
/
VAR jobno NUMBER;
BEGIN
DBMS_JOB.SUBMIT(:jobno, 'archtemp;', SYSDATE, 'sysdate + 1/1440');
COMMIT;
END;
/
I guess it's another job failing, not yours.
You don't do any data fetch here, but I guess some ON UPDATE trigger on ARCH_TEMP table might. Check it.
I'd use a SERVERERROR trigger (as described here) to try to catch the statement that is failing. But first, you could check the alert log. If recursive SQL is erroring, there may be a problem in the data dictionary.
Try putting in an explicit PL/SQL block as the WHAT parameter.
dbms_job.submit(v_jobno, 'begin archtemp; end;', sysdate, 'sysdate+1/1440');
Here's my test case, which seems to work fine:
create table arch_temp (
arch_date date
);
-- create row to test update
insert into arch_temp (arch_date) values (null);
create or replace procedure archtemp as
begin
update arch_temp set arch_date = sysdate;
commit;
end archtemp;
/
-- test everything works in isoloation
begin
archtemp;
end;
/
select * from arch_temp;
-- arch_date = 10:49:34
select * from user_jobs;
-- no rows returned
declare
v_jobno number;
begin
dbms_job.submit(v_jobno, 'begin archtemp; end;', sysdate, 'sysdate+1/1440');
commit;
dbms_output.put_line('v_jobno: ' || to_char(v_jobno));
end;
/
-- dbms_output...
-- v_jobno: 50520
select * from user_jobs;
-- JOB 50520 returned
-- LAST_DATE = 10:51:11
select * from arch_temp;
-- ARCH_DATE = 10:51:11
I tried solution by Nick Pierpoint as well but it didn't work for me
It looks something is wrong with LUCK because i tried the same thing on another machine having Oracle 9i and it failed!!!
Thank you all for your replies.
Regards

Resources