Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

Uma instrução CASE e um DECODE são equivalentes?


Resposta curta, não.

A resposta um pouco mais longa é quase.

Ele apenas aparece que o resultado obtido de cada afirmação é idêntico. Se usarmos a função DUMP para avaliar os tipos de dados retornados, você verá o que quero dizer:
SQL> select dump(case 1 when 2 then null else 0 end) as simple_case
  2       , dump(case when 1 = 2 then null else 0 end) as searched_case
  3       , dump(decode(1, 2, null, 0)) as decode
  4    from dual;

SIMPLE_CASE        SEARCHED_CASE      DECODE
------------------ ------------------ -----------------
Typ=2 Len=1: 128   Typ=2 Len=1: 128   Typ=1 Len=1: 48

SQL Fiddle

Você pode ver que o tipo de dados do DECODE é 1, enquanto as duas instruções CASE "retornam" um tipo de dados de 2. Usando o Resumo de Tipos de Dados do Oracle, DECODE está retornando um VARCHAR2 (tipo de dados 1) enquanto as instruções CASE estão "retornando " números (tipo de dados 2).

Presumo que isso ocorra porque, como os nomes sugerem, DECODE é uma função e CASE não é, o que implica que eles foram implementados de forma diferente internamente. Não há nenhuma maneira real de provar isso.

Você pode pensar que isso realmente não afeta nada. Se você precisar que seja um número, o Oracle converterá implicitamente o caractere em um número sob as regras de conversão implícitas, certo? Isso também não é verdade, não funcionará em um UNION, pois os tipos de dados têm ser idêntico; A Oracle não fará nenhuma conversão implícita para facilitar as coisas para você. Em segundo lugar, aqui está o que a Oracle diz sobre conversão implícita:

A Oracle recomenda que você especifique conversões explícitas, em vez de confiar em conversões implícitas ou automáticas, por estes motivos:

  • As instruções SQL são mais fáceis de entender quando você usa funções de conversão de tipo de dados explícitas.

  • A conversão implícita de tipo de dados pode ter um impacto negativo no desempenho, especialmente se o tipo de dados de um valor de coluna for convertido em uma constante e não o contrário.

  • A conversão implícita depende do contexto em que ocorre e pode não funcionar da mesma maneira em todos os casos. Por exemplo, a conversão implícita de um valor datetime para um valor VARCHAR2 pode retornar um ano inesperado dependendo do valor do parâmetro NLS_DATE_FORMAT.

  • Os algoritmos para conversão implícita estão sujeitos a alterações nas versões de software e entre os produtos Oracle. O comportamento de conversões explícitas é mais previsível.

Essa não é uma lista bonita; mas o penúltimo ponto me traz bem para as datas. Se pegarmos a consulta anterior e a convertermos em uma que use uma data:
select case sysdate when trunc(sysdate) then null 
                    else sysdate 
       end as simple_case
     , case when sysdate = trunc(sysdate) then null 
            else sysdate 
       end as searched_case
     , decode(sysdate, trunc(sysdate), null, sysdate) as decode
  from dual;

Mais uma vez, usando DUMP nesta consulta, as instruções CASE retornam o tipo de dados 12, um DATE. O DECODE converteu sysdate em um VARCHAR2.
SQL> select dump(case sysdate when trunc(sysdate) then null
  2                           else sysdate
  3              end) as simple_case
  4       , dump(case when sysdate = trunc(sysdate) then null
  5                   else sysdate
  6              end) as searched_case
  7       , dump(decode(sysdate, trunc(sysdate), null, sysdate)) as decode
  8    from dual;

SIMPLE_CASE          
---------------------------------- 
Typ=12 Len=7: 120,112,12,4,22,18,7 
SEARCHED_CASE
---------------------------------- 
Typ=12 Len=7: 120,112,12,4,22,18,7
DECODE
---------------------------------- 
Typ=1 Len=19: 50,48,49,50,45,49,50,45,48,52,32,50,49,58,49,55,58,48,54

SQL Fiddle

Observe (no SQL Fiddle) que o DATE foi convertido em um caractere usando as sessões NLS_DATE_FORMAT.

Ter uma data que foi convertida implicitamente em um VARCHAR2 pode causar problemas. Se você pretende usar TO_CHAR, para converter sua data em um caractere, sua consulta será interrompida onde você não esperava.
SQL> select to_char( decode( sysdate
  2                         , trunc(sysdate), null
  3                         , sysdate )
  4                 , 'yyyy-mm-dd') as to_char
  5    from dual;
select to_char( decode( sysdate
                *
ERROR at line 1:
ORA-01722: invalid number

SQL Fiddle

Da mesma forma, a aritmética de datas não funciona mais:
SQL>
SQL>
SQL> select decode(sysdate, trunc(sysdate), null, sysdate) + 1 as decode
  2    from dual;
select decode(sysdate, trunc(sysdate), null, sysdate) + 1 as decode
       *
ERROR at line 1:
ORA-01722: invalid number

SQL Fiddle

Curiosamente, DECODE só converte a expressão para um VARCHAR2 se um dos resultados possíveis for NULL. Se o valor padrão for NULL, isso não acontecerá. Por exemplo:
SQL> select decode(sysdate, sysdate, sysdate, null) as decode
  2    from dual;

DECODE
-------------------
2012-12-04 21:18:32

SQL> select dump(decode(sysdate, sysdate, sysdate, null)) as decode
  2    from dual;

DECODE
------------------------------------------    
Typ=13 Len=8: 220,7,12,4,21,18,32,0

SQL Fiddle

Observe que o DECODE retornou um tipo de dados de 13. Isso não está documentado, mas é, suponho, um tipo de data como aritmética de data etc. funciona.

Resumindo, evite o DECODE se puder; você pode não obter necessariamente os tipos de dados esperados. Citando Tom Kyte:

A decodificação é um pouco obscura -- CASE é muito, muito claro. Coisas que são fáceis de fazer em decodificação são fáceis de fazer em CASE, coisas que são difíceis ou quase impossíveis de fazer com decodificação são fáceis de fazer em CASE. CASE, logicamente, ganha sem dúvida.

Apenas para completar, existem dois funcionais diferenças entre DECODE e CASE.
  1. DECODE não pode ser usado em PL/SQL.

  2. CASE não pode ser usado para comparar nulos diretamente
    SQL> select case null when null then null else 1 end as case1
      2        , case when null is null then null else 1 end as case2
      3        , decode(null, null, null, 1) as decode
      4    from dual
      5         ;
    
         CASE1      CASE2 DECODE
    ---------- ---------- ------
             1
    

    SQL Fiddle