ISNULL vs COALESCE
들어가며
SQL Server에서 NULL 값을 대체할 때 ISNULL과 COALESCE 두 가지 선택지가 있다. 둘 다 “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)
@a가 VARCHAR(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
_email이 verylongemail@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의 핵심 차이를 정리하면:
- 인자 개수: ISNULL은 2개, COALESCE는 여러 개
- 반환 타입: ISNULL은 첫 번째 인자 타입, COALESCE는 우선순위 높은 타입
- 표준: ISNULL은 SQL Server 전용, COALESCE는 ANSI 표준
- 성능: ISNULL이 약간 빠르지만 체감 어려움
단순히 “ISNULL이 빠르니까”로 선택하지 말고, 데이터 타입과 길이가 다른지 먼저 확인하자. 타입이 다르면 COALESCE가 안전하고, 같으면 ISNULL을 써도 무방하다.
References
- 실무 경험 기반 정리 with Claude
- SQL - Difference between COALESCE and ISNULL?
- COALESCE( ) vs ISNULL( ) in SQL
