Oracle Tuning – SQL: Os Falsos Nulos

Falsos Nulos ocorrem quando ao invés de NULL, é utilizado um valor que nunca seria utilizado normalmente, como “Bruce Wayne” para uma columa NOME_EMPREGADO, “000.000.000.000-00” para uma coluna CPF, ou “31/12/4000” para uma coluna DATA_NASCIMENTO.

É muito comum uma modelagem utilizar Falsos Nulos (propositalmente ou não), para “evitar problemas”. Geralmente estes problemas são comportamentos não esperados – não esperados pelo desenvolvedor, e não pelo Oracle – com valores NULL, e manter a portabilidade da aplicação para outro banco de dados (que de qualquer forma não seria fácil mesmo).

O problema dos Falsos Nulos é que ao computar a seletividade de um SELECT, o Oracle os considera valores normais.

Por exemplo, em uma coluna de data, se é utilizada um Falso Nulo com um valor muito longe da maioria dos outros valores (como “31/12/4000”), o Oracle (mais especificamente, o CBO – Cost Based Optimizer) considera que há valores “de 1998 a 4000”, incluindo nos anos 3999, 3998, e assim por diante.

Vejam a tabela abaixo:

SQL> SELECT COUNT(*) FROM T92;

  COUNT(*)
----------
    182700

SQL> SELECT COUNT(*) FROM T92 WHERE DATA_PEDIDO = TO_DATE('31/12/4000', 'DD/MM/YYYY');

  COUNT(*)
----------
       183

SQL>

Como é mostrado acima, apenas uma pequena parte das linhas possui o Falso Nulo, mas vemos abaixo a consequência – os valores reais estão mais “diluídos”, e o CBO considera que há apenas 4 linhas na consulta dos pedidos de um ano, quando na verdade há 3097.

SQL> SET AUTOTRACE ON EXPLAIN
SQL> SELECT COUNT(*) FROM T92 WHERE DATA_PEDIDO BETWEEN TO_DATE('01/12/2010','DD/MM/YYYY') AND TO_DATE('31/12/2010','DD/MM/YYYY');

  COUNT(*)
----------
      3097


Execution Plan
----------------------------------------------------------
Plan hash value: 103883946

---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |     9 |    93   (3)| 00:00:01 |
|   1 |  SORT AGGREGATE    |      |     1 |     9 |            |          |
|*  2 |   TABLE ACCESS FULL| T92  |     4 |    36 |    93   (3)| 00:00:01 |
---------------------------------------------------------------------------

SQL>

Um Histograma corrigiria essa situação, mas colunas de data geralmente tem uma grande quantidade de valores distintos (10 anos são 3650 dias distintos, que nem os histogramas do 12c comportam), o que inviabiliza um bom histograma.

Mas o melhor é fazer o certo: se removermos os falsos nulos, este problema é resolvido.

SQL> SET AUTOTRACE OFF
SQL> DELETE FROM T92 WHERE DATA_PEDIDO = TO_DATE('31-Dec-4000');

183 rows deleted.

SQL> COMMIT;

Commit complete.

SQL> SET AUTOTRACE ON EXPLAIN
SQL> SELECT COUNT(*) FROM T92 WHERE DATA_PEDIDO BETWEEN TO_DATE('01/12/2010','DD/MM/YYYY') AND TO_DATE('31/12/2010','DD/MM/YYYY');

  COUNT(*)
----------
      3097


Execution Plan
----------------------------------------------------------
Plan hash value: 103883946

---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |     9 |    91   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE    |      |     1 |     9 |            |          |
|*  2 |   TABLE ACCESS FULL| T92  |  3097 | 27873 |    91   (0)| 00:00:01 |
---------------------------------------------------------------------------

SQL>

Leave a Reply

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.