NVL과 Coalesce의 Oracle 차이점


208

Oracle에서 NVL과 Coalesce간에 명백한 차이점이 있습니까?

명백한 차이점은 병합이 매개 변수 목록에서 첫 번째 null이 아닌 항목을 반환하는 반면 nvl은 두 개의 매개 변수 만 사용하고 첫 번째가 null이 아닌 경우 첫 번째를 반환하고 그렇지 않으면 두 번째를 반환한다는 것입니다.

NVL은 통합의 '기본 사례'버전 일 수 있습니다.

뭔가 빠졌습니까?


답변:


312

COALESCEANSI-92표준 의 일부인보다 현대적인 기능입니다 .

NVL이다 Oracle그것은에 도입 된 특정 80어떤 기준이되기 전에 S '이 (가) 있습니다.

두 값의 경우 동의어입니다.

그러나 그것들은 다르게 구현됩니다.

NVL항상 두 인수를 모두 평가하지만 COALESCE일반적으로 첫 번째가 아닌 것을 발견 할 때마다 평가를 중지합니다 NULL(sequence와 같은 예외가 있습니다 NEXTVAL).

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

이 아닌 에도 불구하고 0.5생성하기 때문에 거의 몇 초 동안 실행됩니다 .SYS_GUID()1NULL

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

이것은 이것이 1아님을 이해 NULL하고 두 번째 주장을 평가하지 않습니다.

SYS_GUID의 생성되지 않고 쿼리가 즉시 이루어집니다.


11
그것들은 정확히 동의어가 아닙니다 ... 적어도 주어진 값이 다른 유형이라면 NVL이 암시 적 데이터 유형 캐스팅을한다는 사실에서 차이를 찾을 수 있습니다. 예를 들어, COALESCE를 사용하여 두 개의 NULL 값 (하나는 명시 적으로 설정되고 다른 하나는 NUMBER 유형의 데이터베이스 열에서 가져옴)을 전달하는 동안 오류가 발생했습니다.이 함수는 NVL로 변경하면 사라집니다.
DanielM

170

NVL은 첫 번째 매개 변수의 데이터 유형으로 암시 적 변환을 수행하므로 다음은 오류가 없습니다.

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

COALESCE는 일관된 데이터 유형을 기대합니다.

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

'일관되지 않은 데이터 유형 오류'가 발생합니다.


22

NVL 및 COALESCE는 컬럼이 NULL을 리턴하는 경우 기본값을 제공하는 동일한 기능을 달성하는 데 사용됩니다.

차이점은 다음과 같습니다.

  1. NVL은 2 개의 인수 만 허용하지만 COALESCE는 여러 인수를 취할 수 있습니다
  2. NVL은 인수를 평가하고 널이 아닌 값이 처음 나타날 때 COALESCE가 중지합니다.
  3. NVL은 주어진 첫 번째 인수를 기반으로 암시 적 데이터 유형 변환을 수행합니다. COALESCE는 모든 인수가 동일한 데이터 유형이어야합니다.
  4. COALESCE는 UNION 절을 사용하는 쿼리에서 문제를 제공합니다. 아래 예
  5. COALESCE는 NVL이 Oracle에 따라 ANSI 표준입니다.

세 번째 사례의 예입니다. 다른 경우는 간단합니다.

select nvl('abc',10) from dual; NVL이 숫자 10을 문자열로 암시 적으로 변환하므로 작동합니다.

select coalesce('abc',10) from dual; 일관되지 않은 데이터 유형 : CHAR에 NUMBER 번이 있습니다.

UNION 사용 사례의 예

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

실패 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
      ) ;

성공합니다.

자세한 정보 : http://www.plsqlinformation.com/2016/04/difference-between-nvl-and-coalesce-in-oracle.html


"union"에 특정 문제가 있다고 생각하지 않습니다. 오라클은 기본적으로 하위 쿼리에서 캐스트 null을 char로 입력하려고하고 항목 3에 나열된 동일한 문제가 있습니다 (혼합 데이터) 유형). TO_DATE (NULL)로 변경하면 오류가 발생하지 않을 수 있습니다 (사용중인 Oracle 버전에서 오류를 재현 할 수 없음). 그렇지 않으면 귀하의 답변에 동의하고 감사합니다. :-)
시작일

17

계획 처리에도 차이가 있습니다.

Oracle은 검색 nvl결과와 색인 열을 비교할 때 분기 필터를 연결하여 최적화 된 계획을 구성 할 수 있습니다 .

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)

합병 :

---------------------------------------------------------------------------------------
| 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))

크레딧은 http://www.xt-r.com/2012/03/nvl-coalesce-concatenation.html 로 이동 하십시오 .


6

coalesce ()가 null이 아닌 첫 번째 값으로 평가를 중단하지 않는다는 또 다른 증거 :

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

이것을 실행하고 확인하십시오. my_sequence.currval;


5

사실 나는 각 진술에 동의 할 수 없습니다.

"COALESCE는 모든 인수가 동일한 데이터 유형이어야합니다."

이것은 잘못입니다. 아래를 참조하십시오. 인수는 다른 데이터 유형일 수 있으며 문서화됩니다 . expr의 모든 항목이 숫자 데이터 유형이거나 숫자 데이터 유형으로 내재적으로 변환 될 수있는 숫자가 아닌 데이터 유형 인 경우 Oracle Database는 가장 높은 숫자 우선 순위를 갖는 인수를 내재적으로 판별합니다. 나머지 인수를 해당 데이터 유형으로 변환하고 해당 데이터 유형을 리턴합니다. . 실제로 이것은 일반 표현식 "Null이 아닌 값이 처음 발생할 때 COALESCE가 중지됨"과 모순됩니다. 그렇지 않으면 테스트 케이스 4가 오류를 발생시키지 않아야합니다.

또한 테스트 사례 번호 5에 따르면 COALESCE암시적인 인수 변환이 수행됩니다.

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
Re : 테스트 4는 "COALESCE가 널이 아닌 첫 번째 값에서 평가를 중지합니다"와 모순됩니다 . 동의하지 않습니다. 테스트 4는 컴파일러가 COALESCE와의 데이터 유형 일관성을 검사 함을 보여줍니다. 널이 아닌 첫 번째 값에서 중지하는 것은 컴파일 타임 문제가 아니라 런타임 문제입니다. 컴파일 타임에 컴파일러는 세 번째 값 (예 : null)이 널이 아님을 알지 못합니다. 네 번째 값이 실제로 평가되지 않더라도 네 번째 인수도 올바른 데이터 유형이어야한다고 주장합니다.
mathguy

3

이것은 분명하지만이 질문을 한 Tom이 말한 방식으로 언급되었습니다. 그러나 다시 참아 보자.

NVL은 2 개의 인수 만 가질 수 있습니다. 병합에는 2 개 이상이있을 수 있습니다.

select nvl('','',1) from dual;// 결과 : ORA-00909: 잘못된 개수의 인수
select coalesce('','','1') from dual; // 출력 : 1을 반환


3

NVL : 널을 값으로 바꾸십시오 .

COALESCE : 표현식 목록에서 널이 아닌 첫 번째 표현식을 리턴하십시오.

표 : PRICE_LIST

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

아래는

[1] 모든 제품에 10 %의 이익을 더한 판매 가격 설정 의 예입니다 .
[2] 구매 정가가없는 경우 판매 가격이 최소 가격입니다. 정리 세일.
[3] 최소 가격이 없으면 판매 가격을 기본 가격 "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

실제 실제 사례를 설명하십시오.

+----------------+-----------+-----------------+----------------------+
| 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 |
+----------------+-----------+-----------------+----------------------+

NVL을 사용하면 규칙을 달성 할 수 있음을 알 수 있지만 [1], [2]
COALSECE를 사용하면 세 가지 규칙을 모두 달성 할 수 있습니다.


당신이 말하는 것 NVL(Purchase_Price + (Purchase_Price * 0.10), nvl(Min_Price,50)) . 또는 : nvl(NVL(Purchase_Price + (Purchase_Price * 0.10), Min_Price) ,50) :)
Florin Ghita

어느 것이 더 빠르고 성능면에서 사용해야합니까? 로드 할 수천 개의 레코드를 고려하고 있습니까?
rickyProgrammer
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.