Afinal, existe divisão por zero?
Uma das habilidades mais importantes que venho tentando desenvolver enquanto desenvolvedor Oracle é a de realizar um bom teste; um teste que me deixe por a cabeça tranquilamente no travesseiro depois de publicar as melhorias de performance.
Vou dedicar alguns posts para falar disso e hoje gostaria de começar esse tópico.
Um recurso bastante interessante que costumo utilizar em meus testes para verificar se determinados conceitos foram assimilados é a divisão por zero.
Ao falar de performance em aplicações Oracle por aí, já ouvi alguns mitos. Um deles é:
Substitua tudo que estiver na cláusula SELECT da condição EXISTS por NULL que você terá melhoria de performance. Assim:
select 1
from dual
where exists
(select NULL/* estamos falando deste NULL*/
from dual);
Ouvi falar também que o SQL Engine do ORACLE simplesmente ignora o que estiver na cláusula SELECT do EXISTS.
Para saber se isso é verdade, uma das formas é criar uma função e ver ver se realmente a função é chamada de dentro do SELECT da condição EXISTS – uma função que demore intencionalmente um segundo para ser executada pode ajudar.
create or replace function f_funcao_lenta return number is
begin
-- Criamos a demora forçada
dbms_lock.sleep(1);
return 1;
end;
SQL> select 1 col from dual where exists(select f_funcao_lenta from dual);
COL
----------
1
1 row selected.
Elapsed: 00:00:00.02
SQL>
Esse breve teste nos dá uma dica, mas podemos estar lidando com alguma forma de cache desta função, uma vez que estamos numa versão 12c do Oracle Server.
Outra forma de verificar o conceito é então utilizar uma divisão por zero e esperar por um erro ORA-01476: divisor is equal to zero.
SQL> select 1 col from dual where exists(select 1/0 from dual);
COL
----------
1
1 row selected.
Elapsed: 00:00:00.03
SQL>
Neste teste não houve exceção gerada. Num primeiro momento podemos ter a impressão de estarmos diante de um bug, pois não existe divisão por zero e a instrução pergunta claramente se EXISTE 1/0.
O que o Oracle na verdade está respondendo é: SIM, existe ao menos um registro na tabela dual. E essa é a informação relevante para o SQL Engine.
Desta forma, então, o Oracle resolve ignorar a expressão “1/0”.
Se habilitarmos o autotrace do SQL*Plus podemos ter uma dica do que acontece.
SQL> set autot trace
SQL> select 1 col from dual where exists(select 1/0 from tst_exists);
1 row selected.
Elapsed: 00:00:00.03
Execution Plan
----------------------------------------------------------
Plan hash value: 46656579
-------------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 5 (0)| 00:00:01 |
|* 1 | FILTER | | | | |
| 2 | FAST DUAL | | 1 | 2 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL| TST_EXISTS | 1 | 3 (0)| 00:00:01 |
-------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter( EXISTS (SELECT 0 FROM "TST_EXISTS" "TST_EXISTS"))
Note
-----
- dynamic statistics used: dynamic sampling (level=2)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
6 consistent gets
0 physical reads
0 redo size
537 bytes sent via SQL*Net to client
543 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
SQL>
Podemos ver no plano de execução o filtro EXISTS reescrito.
Já rodei esse teste nas versões 10g, 11g e 12c e o resultado é o mesmo: a divisão por zero não interfere no resultado.
Conclusão: Não baseie suas estratégias de performance em reescrever as cláusulas SELECTS das condições EXISTS. O máximo que você pode economizar de tempo será durante o parse da query.
Por hoje á só. Até o próximo post ou comentário.