.comment-link {margin-left:.6em;}

Wednesday, May 04, 2005

Flashback - Introdução

Flashback - podemos chamar essa funcionalidade de salvadora para dedinhos nervosos! Ela permite recuperar tanto informação corrompida, quanto recuperar uma tabela que foi acidentalmente excluída (dropped). Discutiremos recuperação de tabelas dropadas nos próximos dias.

Como funciona? Flashback é possível através do uso da característica de leitura consistente que o Oracle possui. Ele garante que seja visto a informação que estava em dado momento no banco, mesmo que a tabela já não tenha registros. Considere:


ops$marcio@ORA10G> select * from t;

X
-------------
1
2
3
4
5
6
7
8
9
10

10 rows selected.

ops$marcio@ORA10G> var refx refcursor
ops$marcio@ORA10G> exec open :refx for select * from t;

PL/SQL procedure successfully completed.


Neste momento, fiz um select num ponto no tempo, não importa quantos registros serão inseridos ou excluídos da tabela depois, eu quando fizer fetch no cursor irei ver os 10 registros que existiam na tabela t quando fiz a query. Na seqüência, vou excluir todos os registros da tabela e commitar.

ops$marcio@ORA10G> delete from t;

10 rows deleted.

ops$marcio@ORA10G> commit;

Commit complete.

ops$marcio@ORA10G> select * from t;

no rows selected

Acabou. Não há mais registros na tabela, porém:

ops$marcio@ORA10G> print refx

X
-------------
1
2
3
4
5
6
7
8
9
10

10 rows selected.

Meu cursor está exatamente como no momento da query. Isso é leitura consistente. Vim até aqui para embasar o conceito que o Oracle usa para o flashback, nada mais que habilitar aos usuários a capacidade de, mesmo sem ter feito a leitura consistente em dado momento, ser capaz de fazê-lo quando conveniente.

Na versão 9i, flashback estava habilitado somente para queries - o que estamos tratando neste artigo - já a partir da 10g, A Oracle introduziu o flashback de tabela, onde é possível a recuperação de uma tabela que foi dropada.

Acredito que através de exemplos, fica mais fácil a visualização. Na prática, podemos fazer uso do flashback de duas formas: um ponto no tempo, onde o momento que se deseja ver os dados é conhecido - a n unidade de tempo atrás - e através do controle de SCN pela package dbms_flashback. Lembre-se que o flashback depende da capacidade de armazenamento do seu segmento de UNDO (procure por undo_retention) e pela package dbms_flashback.

Exemplos:

Primeiro, vamos declarar duas variáveis para manter o SCN do início e do fim do bloco onde vamos bagunçar a tabela emp do esquema scott.

ops$marcio@ORA10G> var scn_ini number
ops$marcio@ORA10G> var scn_fim number
ops$marcio@ORA10G>
ops$marcio@ORA10G> begin
2 :scn_ini := dbms_flashback.get_system_change_number;
3
4 for i in 1 .. 100
5 loop
6 update scott.emp set sal = sal * 1.01;
7 commit;
8 end loop;
9
10 :scn_fim := dbms_flashback.get_system_change_number;
11 commit;
12 end;
13 /

PL/SQL procedure successfully completed.


SCN_FIM
-------------
1190647


SCN_INI
-------------
1190428



Temos então o scn INICIO e FIM e o tempo (mais ou menos 5 minutos)
Agora, declarar uma variável refcursor para fazer as queries. E ver o resultado da bagunça do bloco pl/sql anterior.

ops$marcio@ORA10G> variable x refcursor
ops$marcio@ORA10G> set autoprint on
ops$marcio@ORA10G>
ops$marcio@ORA10G> select ename, sal from scott.emp;

ENAME SAL
---------- -------------
SMITH 2163,82
ALLEN 4327,72
WARD 3381,09
JONES 8046,79
MARTIN 3381,09
BLAKE 7708,73
CLARK 6626,78
SCOTT 8114,43
KING 13524,08
TURNER 4057,24
ADAMS 2975,28
JAMES 2569,53
FORD 8114,43
MILLER 3516,27

14 rows selected.

Abaixo, passamos o scn inicial e teremos uma posição anterior a vista acima. Seria possível também, pegar um scn "no meio", porque cada commit eleva o SCN, então entre 1190647 e 1190428 poderíamos ver diversos resultados diferentes.

ops$marcio@ORA10G>
ops$marcio@ORA10G> declare
2 l_scn number;
3 begin
4 l_scn := :scn_ini;
5 dbms_flashback.enable_at_system_change_number( l_scn );
6 open :x for select ename, sal from scott.emp;
7 dbms_flashback.disable;
8 end;
9 /

PL/SQL procedure successfully completed.


ENAME SAL
---------- -------------
SMITH 800
ALLEN 1600
WARD 1250
JONES 2975
MARTIN 1250
BLAKE 2850
CLARK 2450
SCOTT 3000
KING 5000
TURNER 1500
ADAMS 1100
JAMES 950
FORD 3000
MILLER 1300

14 rows selected.

A seguir, passando o último scn, teremos o mesmo resultado que a tabela atual.

ops$marcio@ORA10G>
ops$marcio@ORA10G> declare
2 l_scn number;
3 begin
4 l_scn := :scn_fim;
5 dbms_flashback.enable_at_system_change_number( l_scn );
6 open :x for select ename, sal from scott.emp;
7 dbms_flashback.disable;
8 end;
9 /

PL/SQL procedure successfully completed.


ENAME SAL
---------- -------------
SMITH 2163,82
ALLEN 4327,72
WARD 3381,09
JONES 8046,79
MARTIN 3381,09
BLAKE 7708,73
CLARK 6626,78
SCOTT 8114,43
KING 13524,08
TURNER 4057,24
ADAMS 2975,28
JAMES 2569,53
FORD 8114,43
MILLER 3516,27

14 rows selected.

Usando o tempo a sintaxe é bem mais simples e legível, abaixo estamos pedindo 5 minutos atrás - 5/24/60. Mais abaixo, podemos ver um JOIN entre a tabela atual e sua passada, onde serão mostrados os valores dos salários antigos e atuais.

ops$marcio@ORA10G>
ops$marcio@ORA10G> select ename, sal
2 from scott.emp as of timestamp sysdate - 5/24/60
3 /

ENAME SAL
---------- -------------
SMITH 800
ALLEN 1600
WARD 1250
JONES 2975
MARTIN 1250
BLAKE 2850
CLARK 2450
SCOTT 3000
KING 5000
TURNER 1500
ADAMS 1100
JAMES 950
FORD 3000
MILLER 1300

14 rows selected.

ops$marcio@ORA10G>
ops$marcio@ORA10G> select a.ename, a.sal new_sal, b.sal old_sal
2 from scott.emp a, scott.emp as of timestamp sysdate-5/24/60 b
3 where a.empno = b.empno
4 /

ENAME NEW_SAL OLD_SAL
---------- ------------- -------------
SMITH 2163,82 800
ALLEN 4327,72 1600
WARD 3381,09 1250
JONES 8046,79 2975
MARTIN 3381,09 1250
BLAKE 7708,73 2850
CLARK 6626,78 2450
SCOTT 8114,43 3000
KING 13524,08 5000
TURNER 4057,24 1500
ADAMS 2975,28 1100
JAMES 2569,53 950
FORD 8114,43 3000
MILLER 3516,27 1300

14 rows selected.

Agora vamos acertar as coisas depois do disastre! Utilizando a mesma técnica acima, usando o passado da tabela para consertar as coisas. Ah se nós tivessemos essa capacidade? Quantas coisas você não consertaria na sua vida heim? Dava pra casar várias vezes, não deu certo, ahhh volta! :)

update vida.marcio
set estado_civil = ( select estado_civil
from vida.marcio as of timestamp sysdate - 10 years );

Ou melhor ainda!

update vida.marcio
set peso = ( select peso
from vida.marcio as of timestamp sysdate - 10 years );

Voltando a realidade!

ops$marcio@ORA10G> update scott.emp
2 set sal = ( select sal
3 from scott.emp as of timestamp sysdate-5/24/60 b
4 where b.empno = emp.empno )
5 /

14 rows updated.

ops$marcio@ORA10G> commit;

Commit complete.

ops$marcio@ORA10G>
ops$marcio@ORA10G> select ename, sal from scott.emp;

ENAME SAL
---------- -------------
SMITH 800
ALLEN 1600
WARD 1250
JONES 2975
MARTIN 1250
BLAKE 2850
CLARK 2450
SCOTT 3000
KING 5000
TURNER 1500
ADAMS 1100
JAMES 950
FORD 3000
MILLER 1300

14 rows selected.



Uma vez entendido o funcionamento do segmento de UNDO e as leituras consistentes, o flashback é conseqüência.

Abraço,
Comments:
Perfeito exemplo Márcio, eu estava precisando de algo parecido pra passar para os meus alunos. Sow de bola.
 
Feliz aniversário Vander, eu já sabia que era isso que voce estava precisando ;) então fica como presente de aniversário.
 
Pow Márcio, brigadão cara, valeu mesmo.
 
Post a Comment



<< Home

This page is powered by Blogger. Isn't yours?