Diferenças do Oracle entre NVL e Coalesce


208

Existem diferenças não óbvias entre NVL e Coalesce no Oracle?

As diferenças óbvias são que a coalescência retornará o primeiro item não nulo em sua lista de parâmetros, enquanto nvl usa apenas dois parâmetros e retorna o primeiro se não for nulo; caso contrário, retorna o segundo.

Parece que o NVL pode ser apenas uma versão do 'Case Base' de coalescência.

Estou esquecendo de algo?


Respostas:


312

COALESCEé uma função mais moderna que faz parte do ANSI-92padrão.

NVLé Oracleespecífico, foi introduzido em 80antes de haver padrões.

No caso de dois valores, eles são sinônimos.

No entanto, eles são implementados de maneira diferente.

NVLsempre avalia os dois argumentos, enquanto COALESCEgeralmente interrompe a avaliação sempre que encontra a primeira não NULL(existem algumas exceções, como sequência NEXTVAL):

SELECT  SUM(val)
FROM    (
        SELECT  NVL(1, LENGTH(RAWTOHEX(SYS_GUID()))) AS val
        FROM    dual
        CONNECT BY
                level <= 10000
        )

Isso funciona por quase 0.5segundos, uma vez que gera SYS_GUID(), apesar de 1não ser um NULL.

SELECT  SUM(val)
FROM    (
        SELECT  COALESCE(1, LENGTH(RAWTOHEX(SYS_GUID()))) AS val
        FROM    dual
        CONNECT BY
                level <= 10000
        )

Isso entende que 1não é um NULLe não avalia o segundo argumento.

SYS_GUIDnão são gerados e a consulta é instantânea.


11
Eles não são exatamente sinônimos ... Pelo menos você pode encontrar uma diferença no fato de o NVL fazer uma conversão implícita de tipo de dados se os valores fornecidos forem de tipos diferentes. Por exemplo, eu estava recebendo um erro usando COALESCE passando dois valores NULL (um explicitamente definido e o outro retirado de uma coluna no banco de dados, do tipo NUMBER), que simplesmente desaparecem alterando a função para NVL.
precisa saber é o seguinte

170

O NVL fará uma conversão implícita no tipo de dados do primeiro parâmetro, portanto, o seguinte não erro

select nvl('a',sysdate) from dual;

O COALESCE espera tipos de dados consistentes.

select coalesce('a',sysdate) from dual;

lançará um 'erro de tipo de dados inconsistente'


22

NVL e COALESCE são usados ​​para obter a mesma funcionalidade de fornecer um valor padrão, caso a coluna retorne um NULL.

As diferenças são:

  1. NVL aceita apenas 2 argumentos, enquanto COALESCE pode receber vários argumentos
  2. O NVL avalia os argumentos e COALESCE para na primeira ocorrência de um valor não nulo.
  3. O NVL faz uma conversão implícita de tipo de dados com base no primeiro argumento fornecido. COALESCE espera que todos os argumentos sejam do mesmo tipo de dados.
  4. COALESCE fornece problemas em consultas que usam cláusulas UNION. Exemplo abaixo
  5. COALESCE é o padrão ANSI onde, como o NVL, é específico do Oracle.

Exemplos para o terceiro caso. Outros casos são simples.

select nvl('abc',10) from dual; funcionaria como o NVL fará uma conversão implícita do numérico 10 em string.

select coalesce('abc',10) from dual; falhará com Erro - tipos de dados inconsistentes: CHAR esperado obteve NUMBER

Exemplo para caso de uso UNION

SELECT COALESCE(a, sysdate) 
from (select null as a from dual 
      union 
      select null as a from dual
      );

falha com ORA-00932: inconsistent datatypes: expected CHAR got DATE

SELECT NVL(a, sysdate) 
from (select null as a from dual 
      union 
      select null as a from dual
      ) ;

consegue.

Mais informações: http://www.plsqlinformation.com/2016/04/difference-between-nvl-and-coalesce-in-oracle.html


Eu não acho que exista um problema específico com a "união" que pareça que o Oracle queira digitar cast null na subconsulta a um char por padrão e então você tenha o mesmo problema listado no item 3 (dados mistos) tipos). Se você o alterar para TO_DATE (NULL), provavelmente não receberá o erro (não consigo reproduzir o erro na versão do Oracle que estou usando). Caso contrário, concordo e aprecio sua resposta. :-)
splashout 22/01

17

Também há diferença no manuseio do plano.

O Oracle pode formar um plano otimizado com concatenação de filtros de ramificação quando a pesquisa contém comparação de nvlresultado com uma coluna indexada.

create table tt(a, b) as
select level, mod(level,10)
from dual
connect by level<=1e4;

alter table tt add constraint ix_tt_a primary key(a);
create index ix_tt_b on tt(b);

explain plan for
select * from tt
where a=nvl(:1,a)
  and b=:2;

explain plan for
select * from tt
where a=coalesce(:1,a)
  and b=:2;

nvl:

-----------------------------------------------------------------------------------------
| Id  | Operation                     | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |         |     2 |    52 |     2   (0)| 00:00:01 |
|   1 |  CONCATENATION                |         |       |       |            |          |
|*  2 |   FILTER                      |         |       |       |            |          |
|*  3 |    TABLE ACCESS BY INDEX ROWID| TT      |     1 |    26 |     1   (0)| 00:00:01 |
|*  4 |     INDEX RANGE SCAN          | IX_TT_B |     7 |       |     1   (0)| 00:00:01 |
|*  5 |   FILTER                      |         |       |       |            |          |
|*  6 |    TABLE ACCESS BY INDEX ROWID| TT      |     1 |    26 |     1   (0)| 00:00:01 |
|*  7 |     INDEX UNIQUE SCAN         | IX_TT_A |     1 |       |     1   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter(:1 IS NULL)
   3 - filter("A" IS NOT NULL)
   4 - access("B"=TO_NUMBER(:2))
   5 - filter(:1 IS NOT NULL)
   6 - filter("B"=TO_NUMBER(:2))
   7 - access("A"=:1)

coalescer:

---------------------------------------------------------------------------------------
| Id  | Operation                   | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |         |     1 |    26 |     1   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS BY INDEX ROWID| TT      |     1 |    26 |     1   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IX_TT_B |    40 |       |     1   (0)| 00:00:01 |
---------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("A"=COALESCE(:1,"A"))
   2 - access("B"=TO_NUMBER(:2))

Os créditos vão para http://www.xt-r.com/2012/03/nvl-coalesce-concatenation.html .


6

Outra prova de que coalescer () não interrompe a avaliação com o primeiro valor não nulo:

SELECT COALESCE(1, my_sequence.nextval) AS answer FROM dual;

Execute isso e verifique my_sequence.currval;


5

Na verdade, não posso concordar com cada afirmação.

"COALESCE espera que todos os argumentos sejam do mesmo tipo de dados."

Isso está errado, veja abaixo. Os argumentos podem ser tipos de dados diferentes, também documentados : Se todas as ocorrências de expr forem do tipo numérico ou qualquer tipo de dado não numérico que possa ser implicitamente convertido em um tipo de dado numérico, o Oracle Database determinará o argumento com a maior precedência numérica, implicitamente converte os argumentos restantes para esse tipo de dados e retorna esse tipo de dados. . Na verdade, isso contradiz a expressão comum "COALESCE para na primeira ocorrência de um valor não nulo"; caso contrário, o caso de teste n ° 4 não deve gerar um erro.

Também de acordo com o caso de teste n ° 5, COALESCEfaz uma conversão implícita de argumentos.

DECLARE
    int_val INTEGER := 1;
    string_val VARCHAR2(10) := 'foo';
BEGIN

    BEGIN
    DBMS_OUTPUT.PUT_LINE( '1. NVL(int_val,string_val) -> '|| NVL(int_val,string_val) );
    EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('1. NVL(int_val,string_val) -> '||SQLERRM ); 
    END;

    BEGIN
    DBMS_OUTPUT.PUT_LINE( '2. NVL(string_val, int_val) -> '|| NVL(string_val, int_val) );
    EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('2. NVL(string_val, int_val) -> '||SQLERRM ); 
    END;

    BEGIN
    DBMS_OUTPUT.PUT_LINE( '3. COALESCE(int_val,string_val) -> '|| COALESCE(int_val,string_val) );
    EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('3. COALESCE(int_val,string_val) -> '||SQLERRM ); 
    END;

    BEGIN
    DBMS_OUTPUT.PUT_LINE( '4. COALESCE(string_val, int_val) -> '|| COALESCE(string_val, int_val) );
    EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('4. COALESCE(string_val, int_val) -> '||SQLERRM ); 
    END;

    DBMS_OUTPUT.PUT_LINE( '5. COALESCE(SYSDATE,SYSTIMESTAMP) -> '|| COALESCE(SYSDATE,SYSTIMESTAMP) );

END;
Output:

1. NVL(int_val,string_val) -> ORA-06502: PL/SQL: numeric or value error: character to number conversion error
2. NVL(string_val, int_val) -> foo
3. COALESCE(int_val,string_val) -> 1
4. COALESCE(string_val, int_val) -> ORA-06502: PL/SQL: numeric or value error: character to number conversion error
5. COALESCE(SYSDATE,SYSTIMESTAMP) -> 2016-11-30 09:55:55.000000 +1:0 --> This is a TIMESTAMP value, not a DATE value!

1
O teste 4 contradiz "COALESCE interrompe a avaliação no primeiro valor não nulo" . Discordo. O teste 4 mostra que o compilador verifica a consistência do tipo de dados com COALESCE. Parar no primeiro valor não nulo é um problema de tempo de execução, não um problema de tempo de compilação. No momento da compilação, o compilador não sabe que o terceiro valor (digamos) será não nulo; insiste em que o quarto argumento também seja do tipo de dados correto, mesmo que esse quarto valor nunca seja realmente avaliado.
mathguy

3

Embora este seja óbvio, e até mencionado de uma maneira apresentada por Tom, que fez essa pergunta. Mas vamos colocar de novo.

NVL pode ter apenas 2 argumentos. A coalescência pode ter mais de 2.

select nvl('','',1) from dual;// Resultado:: ORA-00909número inválido de argumentos
select coalesce('','','1') from dual; // Saída: retorna 1


3

NVL: Substitua nulo por valor.

COALESCE: Retorne a primeira expressão não nula da lista de expressões.

Tabela: PRICE_LIST

+----------------+-----------+
| Purchase_Price | Min_Price |
+----------------+-----------+
| 10             | null      |
| 20             |           |
| 50             | 30        |
| 100            | 80        |
| null           | null      |
+----------------+-----------+   

Abaixo está o exemplo de

[1] Defina o preço de venda com a adição de 10% de lucro a todos os produtos.
[2] Se não houver preço de tabela de compra, o preço de venda é o preço mínimo. Para liquidação.
[3] Se também não houver preço mínimo, defina o preço de venda como preço padrão "50".

SELECT
     Purchase_Price,
     Min_Price,
     NVL(Purchase_Price + (Purchase_Price * 0.10), Min_Price)    AS NVL_Sales_Price,
COALESCE(Purchase_Price + (Purchase_Price * 0.10), Min_Price,50) AS Coalesce_Sales_Price
FROM 
Price_List

Explique com um exemplo prático da vida real.

+----------------+-----------+-----------------+----------------------+
| Purchase_Price | Min_Price | NVL_Sales_Price | Coalesce_Sales_Price |
+----------------+-----------+-----------------+----------------------+
| 10             | null      | 11              |                   11 |
| null           | 20        | 20              |                   20 |
| 50             | 30        | 55              |                   55 |
| 100            | 80        | 110             |                  110 |
| null           | null      | null            |                   50 |
+----------------+-----------+-----------------+----------------------+

Você pode ver que, com o NVL, podemos alcançar as regras [1], [2],
mas com o COALSECE, podemos alcançar as três regras.


sobre o que você diz NVL(Purchase_Price + (Purchase_Price * 0.10), nvl(Min_Price,50)) . Ou sobre: nvl(NVL(Purchase_Price + (Purchase_Price * 0.10), Min_Price) ,50) :)
Florin Ghita

o que é mais rápido, em termos de desempenho, o que deve ser usado? considerando milhares de registros para carregar?
precisa saber é o seguinte
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.