Post

ISNULL vs COALESCE

ISNULL vs COALESCE

들어가며

SQL Server에서 NULL 값을 대체할 때 ISNULLCOALESCE 두 가지 선택지가 있다. 둘 다 “NULL이면 다른 값으로 바꿔줘”라는 같은 목적을 가지고 있지만, 내부 동작 방식과 세부 특성이 다르다.

단순히 “ISNULL이 빠르니까 ISNULL 쓰자”로 끝나는 게 아니다. 반환 타입 차이 때문에 예상치 못한 버그가 발생할 수 있어서, 정확한 차이를 알고 상황에 맞게 선택해야 한다.


1. 기본 문법과 차이

ISNULL

1
ISNULL(check_expression, replacement_value)
  • SQL Server 전용 함수
  • 2개 인자만 받음
  • 첫 번째 값이 NULL이면 두 번째 값 반환
1
2
3
DECLARE @nickname NVARCHAR(50) = NULL

SELECT ISNULL(@nickname, '익명')  -- 결과: '익명'

COALESCE

1
COALESCE(expression1, expression2, ... , expressionN)
  • ANSI SQL 표준 (모든 DB 호환)
  • 여러 개 인자 받을 수 있음
  • NULL이 아닌 첫 번째 값 반환
1
2
3
4
5
DECLARE @a NVARCHAR(50) = NULL
DECLARE @b NVARCHAR(50) = NULL
DECLARE @c NVARCHAR(50) = '기본값'

SELECT COALESCE(@a, @b, @c, '최종 기본값')  -- 결과: '기본값'

COALESCE는 인자를 순서대로 확인하면서 NULL이 아닌 첫 번째 값을 반환한다. 모두 NULL이면 마지막 값(이것도 NULL일 수 있음)을 반환한다.


2. 반환 타입 차이

이 부분이 실무에서 버그로 이어지는 핵심 차이다.

ISNULL: 첫 번째 인자의 타입을 따른다

1
2
3
4
5
6
DECLARE @a VARCHAR(10) = NULL
DECLARE @b VARCHAR(100) = 'hello world test'

SELECT ISNULL(@a, @b)
-- 결과: 'hello worl' (10자로 잘림!)
-- 반환 타입: VARCHAR(10)

@aVARCHAR(10)이므로, 결과도 VARCHAR(10)이 된다. @b의 값이 아무리 길어도 첫 번째 인자의 타입 길이로 잘린다.

COALESCE: 우선순위 높은 타입을 따른다

1
2
3
4
5
6
DECLARE @a VARCHAR(10) = NULL
DECLARE @b VARCHAR(100) = 'hello world test'

SELECT COALESCE(@a, @b)
-- 결과: 'hello world test' (전체 출력)
-- 반환 타입: VARCHAR(100)

COALESCE는 인자들 중 데이터 타입 우선순위가 가장 높은 타입 으로 결과를 반환한다. 길이도 가장 큰 것을 따른다.

실무에서 발생하는 버그 예시

1
2
3
4
5
6
7
8
9
10
11
12
-- 회원 테이블
CREATE TABLE TblMember (
    _memberId INT,
    _nickname NVARCHAR(10),  -- 최대 10자
    _email NVARCHAR(100)
)

-- 의도: 닉네임이 없으면 이메일 앞부분을 표시
SELECT 
    _memberId,
    ISNULL(_nickname, _email) AS _displayName  -- 버그!
FROM TblMember

_emailverylongemail@example.com이라면, ISNULL 결과는 verylonge(10자)로 잘린다.

1
2
3
4
5
-- 수정: COALESCE 사용
SELECT 
    _memberId,
    COALESCE(_nickname, _email) AS _displayName  -- 정상
FROM TblMember

서로 다른 길이의 문자열 컬럼을 비교할 때는 COALESCE가 안전하다.


3. 내부 동작과 성능

ISNULL: 단순 함수

ISNULL은 SQL Server 내장 함수로, 직접 NULL 체크 후 값을 반환한다.

COALESCE: CASE 문으로 변환

COALESCE는 내부적으로 CASE 문으로 변환되어 실행된다.

1
2
3
4
5
6
7
8
9
-- 이 쿼리는
COALESCE(@a, @b, @c)

-- 내부적으로 이렇게 변환됨
CASE 
    WHEN @a IS NOT NULL THEN @a
    WHEN @b IS NOT NULL THEN @b
    ELSE @c
END

성능 차이는?

이론적으로 ISNULL이 더 빠르다. 하지만 체감할 정도의 차이는 거의 없다.

1
2
3
-- 100만 건 테스트 시
-- ISNULL: ~150ms
-- COALESCE: ~160ms

수백만 건을 반복 처리하는 배치 작업이 아니라면, 성능보다는 정확성(타입 안전성) 을 우선시하는 게 맞다.


4. Nullability 차이

SELECT INTO로 테이블을 생성할 때 차이가 발생한다.

1
2
3
4
5
6
7
8
SELECT 
    ISNULL(NULL, 1) AS isnull_col,
    COALESCE(NULL, 1) AS coalesce_col
INTO #TempTable

-- 결과 테이블 구조
-- isnull_col: INT NOT NULL
-- coalesce_col: INT NULL
  • ISNULL: 결과 컬럼을 NOT NULL로 판단
  • COALESCE: 결과 컬럼을 NULL 허용으로 판단

인덱싱된 뷰(Indexed View)나 계산 컬럼에서 이 차이가 영향을 줄 수 있다.


5. 인자가 3개 이상일 때

ISNULL은 2개만 받으므로, 3개 이상 비교하려면 중첩해야 한다.

1
2
3
4
5
-- ISNULL 중첩 (가독성 나쁨)
ISNULL(@a, ISNULL(@b, ISNULL(@c, 'default')))

-- COALESCE (깔끔함)
COALESCE(@a, @b, @c, 'default')

인자가 3개 이상이면 COALESCE가 유일한 선택지 다.

ex) 연락처 우선순위

1
2
3
4
5
-- 연락 가능한 번호를 우선순위대로 반환
SELECT 
    _memberId,
    COALESCE(_mobilePhone, _homePhone, _officePhone, '연락처 없음') AS _contactNumber
FROM TblMember


6. 상황별 선택

상황 권장 이유
인자 2개, 같은 타입 ISNULL 단순하고 약간 빠름
인자 2개, 다른 타입/길이 COALESCE 데이터 잘림 방지
인자 3개 이상 COALESCE ISNULL은 불가능
다른 DB 마이그레이션 가능성 COALESCE ANSI 표준
NOT NULL 보장 필요 ISNULL Nullability 특성


7. 주의사항

서브쿼리 사용 시 COALESCE 주의

COALESCE에 서브쿼리를 넣으면 여러 번 실행될 수 있다.

1
2
3
4
5
6
7
8
9
10
-- 비효율적: 서브쿼리가 2번 실행될 수 있음
SELECT COALESCE(
    (SELECT TOP 1 _value FROM TblConfig WHERE _key = 'setting1'),
    'default'
)

-- 개선: 변수에 먼저 담기
DECLARE @setting NVARCHAR(100)
SELECT @setting = _value FROM TblConfig WHERE _key = 'setting1'
SELECT ISNULL(@setting, 'default')

COALESCE 내부의 표현식은 NULL 여부 확인을 위해 여러 번 평가될 수 있다. 비용이 큰 서브쿼리는 변수로 먼저 받아두는 게 좋다.


마치며

ISNULL과 COALESCE의 핵심 차이를 정리하면:

  1. 인자 개수: ISNULL은 2개, COALESCE는 여러 개
  2. 반환 타입: ISNULL은 첫 번째 인자 타입, COALESCE는 우선순위 높은 타입
  3. 표준: ISNULL은 SQL Server 전용, COALESCE는 ANSI 표준
  4. 성능: ISNULL이 약간 빠르지만 체감 어려움

단순히 “ISNULL이 빠르니까”로 선택하지 말고, 데이터 타입과 길이가 다른지 먼저 확인하자. 타입이 다르면 COALESCE가 안전하고, 같으면 ISNULL을 써도 무방하다.


References

This post is licensed under CC BY 4.0 by the author.