segunda-feira, 5 de dezembro de 2011

DBMS_SCHEDULER Oracle 10g

DEFINIÇÃO
À partir da versao 10g do banco de dados, a Oracle disponibilizou um novo pacote para criação de jobs/schedulers o DBMS_SCHEDULER. Esse pacote não vem para substituir a antiga DBMS_JOB, mas sim para ampliar sua funcionalidade.
Nesse post vou falar um pouco sobre as principais funcionalidades desse novo pacote.

DIFERENÇA EM RELAÇÃO À DBMS_JOB
A principal diferença em relação à DBMS_JOB, é que agora é possível ter uma estrutura mais modular, onde é possível ter o programa/tarefa/rotina que será executada separada do tempo de execução(periodicidade). Assim, temos agora 3 novos termos, programa, schedule e job.

PROGRAMA
Um programa é a tarefa que será executada, seja ela um bloco anônimo, uma Stored Procedure ou um executável do sistema operacional. Ao criar um programa, não se define quando o mesmo será executado, apenas faz-se o registro do quê será executado.
Exemplo:

-- Criando um programa de teste
BEGIN
  -- Bloco PL/SQL .
  DBMS_SCHEDULER.create_program (
    program_name   => 'test_plsql_block_prog',
    program_type   => 'PLSQL_BLOCK',
    program_action => 'BEGIN DBMS_STATS.gather_schema_stats(''SCOTT''); END;',
    enabled        => TRUE,
    comments       => 'Programa para coletar as estatísticas do usuário SCOTT''s usando um bloco PL/SQL.');

  -- Shell Script.
  DBMS_SCHEDULER.create_program (
    program_name        => 'test_executable_prog',
    program_type        => 'EXECUTABLE',
    program_action      => '/u01/app/oracle/dba/gather_scott_stats.sh',
    number_of_arguments => 0,
    enabled             => TRUE,
    comments            => 'Programa para coletar as estatísticas do usuário SCOTT''s usando shell script.');

  -- Stored Procedure com argumentos.
  DBMS_SCHEDULER.create_program (
    program_name        => 'test_stored_procedure_prog',
    program_type        => 'STORED_PROCEDURE',
    program_action      => 'DBMS_STATS.gather_schema_stats',
    number_of_arguments => 1,
    enabled             => FALSE,
    comments            => 'Programa para coletar as estatísticas do usuário SCOTT''s usando uma stored procedure.');

  -- Definindo um argumento para a stored procedure criado anteriormente
  DBMS_SCHEDULER.define_program_argument (
    program_name      => 'test_stored_procedure_prog',
    argument_name     => 'ownname',
    argument_position => 1,
    argument_type     => 'VARCHAR2',
    default_value     => 'SCOTT');

  -- habilitando o programa criado anteriormente
  DBMS_SCHEDULER.enable (name => 'test_stored_procedure_prog');
END;
/
PL/SQL procedure successfully completed.

Podemos em seguida consultar os programas criados no bloco acima:

SELECT owner, program_name, enabled FROM dba_scheduler_programs;

OWNER                          PROGRAM_NAME                   ENABL
------------------------------ ------------------------------ -----
SYS                            PURGE_LOG_PROG                 TRUE
SYS                            GATHER_STATS_PROG              TRUE
SYS                            TEST_PLSQL_BLOCK_PROG          TRUE
SYS                            TEST_EXECUTABLE_PROG           TRUE
SYS                            TEST_STORED_PROCEDURE_PROG     TRUE

5 rows selected.

Podemos verificar na 3ª coluna o status do programa criado, todos são criados como enabled por default. É possível desabilitá-los através da procedure DBMS_SCHEDULER.DISABLE.
Para eliminar um programa, basta executar a procedure abaixo:

   
DBMS_SCHEDULER.drop_program (program_name => 'test_plsql_block_prog');

SCHEDULE
Um schedule define um horário ou período para execução de um/vários programa(s). Essa é a grande sacada dessa nova estrutura de jobs da versão 10g, é possível associar um schedule a mais de programa, o que facilita a administração.
Para criar um schedule, usamos os exemplos abaixo:

-- Criando o schedule.
BEGIN
  DBMS_SCHEDULER.create_schedule (
    schedule_name   => 'test_hourly_schedule',
    start_date      => SYSTIMESTAMP,
    repeat_interval => 'freq=hourly; byminute=0',
    end_date        => NULL,
    comments        => 'Repetir a cada hora, por tempo indeterminado.');
END;
/

PL/SQL procedure successfully completed.

Para consultarmos se um schedule foi realmente criado, utilizamos a query abaixo:

SELECT owner, schedule_name FROM dba_scheduler_schedules;

OWNER                          SCHEDULE_NAME
------------------------------ ------------------------------
SYS                            DAILY_PURGE_SCHEDULE
SYS                            TEST_HOURLY_SCHEDULE

2 rows selected.

Para eliminar um schedule, utilizamos o exemplo abaixo:

BEGIN
  DBMS_SCHEDULER.drop_schedule (schedule_name => 'TEST_HOURLY_SCHEDULE');
END;
/

PL/SQL procedure successfully completed.

JOB
A essas alturas você já deve estar se perguntado: "Pra tanto trabalho pra se criar um job, sendo que até hoje eu criei tudo com uma única linha de comando?". Calma, existe também a possibilidade de criar tudo em um único passo, tudo como era antigamente, a diferença é que agora quando for utilizado a built-in DBMS_SCHEDULER, o job já vai ser criado modularizado, ou seja, tanto a rotina quando o período podem ser reaproveitados.
Vamos ver um pouco mais sobre a variação de formas de se criar um job:

BEGIN
  -- Job criado totalmente através da procedure CREATE JOB.
  DBMS_SCHEDULER.create_job (
    job_name        => 'test_full_job_definition',
    job_type        => 'PLSQL_BLOCK',
    job_action      => 'BEGIN DBMS_STATS.gather_schema_stats(''SCOTT''); END;',
    start_date      => SYSTIMESTAMP,
    repeat_interval => 'freq=hourly; byminute=0',
    end_date        => NULL,
    enabled         => TRUE,
    comments        => 'Job criado totalmente através da procedure CREATE JOB.');

  -- Job criado usando um programa e schedule existentes.
  DBMS_SCHEDULER.create_job (
    job_name      => 'test_prog_sched_job_definition',
    program_name  => 'test_plsql_block_prog',
    schedule_name => 'test_hourly_schedule',
    enabled       => TRUE,
    comments      => 'Job criado usando um programa e schedule existentes.');

  -- Job criado por um programa existente e um schedule inline(não existente).
  DBMS_SCHEDULER.create_job (
    job_name        => 'test_prog_job_definition',
    program_name    => 'test_plsql_block_prog',
    start_date      => SYSTIMESTAMP,
    repeat_interval => 'freq=hourly; byminute=0',
    end_date        => NULL,
    enabled         => TRUE,
    comments        => 'Job criado por um programa existente e um schedule inline(não existente).');

  -- Job criado por um programa e um schedule inline(não existentes).
  DBMS_SCHEDULER.create_job (
     job_name      => 'test_sched_job_definition',
     schedule_name => 'test_hourly_schedule',
     job_type      => 'PLSQL_BLOCK',
     job_action    => 'BEGIN DBMS_STATS.gather_schema_stats(''SCOTT''); END;',
     enabled       => TRUE,
     comments      => 'Job criado por um programa e um schedule inline(não existentes).');
END;
/

PL/SQL procedure successfully completed.

Para confirmar se os jobs foram criados, usamos a query abaixo:

SELECT owner, job_name, enabled FROM dba_scheduler_jobs;

OWNER                          JOB_NAME                       ENABL
------------------------------ ------------------------------ -----
SYS                            PURGE_LOG                      TRUE
SYS                            GATHER_STATS_JOB               TRUE
SYS                            TEST_FULL_JOB_DEFINITION       TRUE
SYS                            TEST_PROG_SCHED_JOB_DEFINITION TRUE
SYS                            TEST_PROG_JOB_DEFINITION       TRUE
SYS                            TEST_SCHED_JOB_DEFINITION      TRUE

6 rows selected.

Ou seja, através dos exemplos acima, vemos que é possível criar os jobs com programas e schedules existentes ou não. Complementando a resposta à questão do início do tópico, eu adiciono uma  outra pergunta: "Você pretende reutilizar o período ou a rotina que será executada?" se a sua resposta foi não, continue utilizando a boa e velha DBMS_JOB, se foi sim....

Para excluir um job, utilizamos o bloco abaixo:

BEGIN
    DBMS_SCHEDULER.drop_job (job_name => 'test_full_job_definition');
END;


CONTROLE DE EXECUÇÃO DE JOBS
Um job tem sua execução de forma assíncrona, ou seja, é o próprio banco quem controla sua execução de acordo com os atributos definidos na criação do mesmo. Porém, é possível ter um controle manual, como abaixo:

BEGIN
  -- Forçando a execução de um job
  DBMS_SCHEDULER.run_job (job_name            => 'test_full_job_definition',
                          use_current_session => FALSE);

  -- Parando a execução de um job
  DBMS_SCHEDULER.stop_job (job_name => 'test_full_job_definition, test_prog_sched_job_definition');
END;
/

PRIVILÉGIOS
Nessa versão de banco, uma nova rule foi criado para controlar o Scheduler, o SCHEDULER_ADMIN. Esse grant inicialmente só é concedido ao DBA e permite ao mesmo qualquer ação sobre um Job criado por ele ou por qualquer outro usuário. Esse grant é poderoso, portanto deve-se ter muito cuidado ao concedê-lo aos usuários. Porém, existe também a rule  MANAGE SCHEDULER, que permite ao usuário consultar atributos de um Job ou alterá-los através das procedures GET_SCHEDULER_ATTRIBUTESET_SCHEDULER_ATTRIBUTE respectivamente.


Artigos relacionados:
http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_sched.htm
http://www.oracle-base.com/articles/10g/Scheduler10g.php


That´s all Folks

Um comentário: