[SQL] NULL이 포함된 연산은 NULL로 평가된다.
연산과 조건절에서의 NULL
업무에서 데이터 조회를 위한 쿼리를 작성하다가, NULL을 잘못 처리하는 실수를 했다. 이번기회에 다시 NULL을 어떻게 처리해야 하는지 공부하고, 고민해보려고한다. 실제 업무의 데이터는 꽤 복잡하고 민감한 정보라, 아래의 예제는 일반적인 상황으로 치환한 사례이다.
아래에서는 "사용자 계정" 정보를 담고 있는 user_accounts
테이블과 "월별 사용량" 정보를 담고 있는 monthly_usage
테이블을 조인하여 특정 사용자의 월별 사용 정보를 검색하는 과정에서 발생한 문제와 해결 방안을 소개한다.
테이블 예시
user_accounts
테이블
account_id | user_name | usage_month |
---|---|---|
0000001 | 홍길동 | 2024-05 |
0000002 | 이순신 | 2024-05 |
0000003 | 강감찬 | 2024-05 |
monthly_usage
테이블
usage_month | account_id | usage_status |
---|---|---|
2024-05 | 0000001 | 1 |
2024-05 | 0000002 | 3 |
문제 발생 및 원인 분석
LEFT JOIN
을 사용하여 두 테이블을 조인할 때, mu.usage_status NOT IN ('3')
조건으로 인해 usage_status
가 NULL인 강감찬의 정보가 결과에서 제외되는 문제가 발생하였다.
SQL에서는 NULL 값을 '값이 없음'으로 처리하며, NULL과의 비교는 TRUE나 FALSE가 아닌 NULL을 결과로 반환한다. 따라서, mu.usage_status
가 NULL인 경우, 해당 조건은 TRUE나 FALSE로 평가되지 않아 결과 집합에서 해당 행이 제외된다.
해결 방안
- 명시적 NULL 처리
명시적으로 NULL 값을 처리하여 문제를 해결할 수 있다. 아래 쿼리는 mu.usage_status
가 NULL인 경우도 결과에 포함한다.
SELECT * FROM user_accounts ua
LEFT OUTER JOIN monthly_usage mu
ON ua.account_id = mu.account_id
WHERE (mu.usage_status NOT IN ('3') OR mu.usage_status IS NULL)
AND ua.usage_month = '2024-05'
NVL
함수 활용
NVL
함수를 사용하여 NULL 값을 특정 값으로 대체하는 방법도 있다. 이 경우, NULL인 mu.usage_status
를 '0'으로 간주하여 조건을 만족시키므로, 모든 사용자 정보가 결과에 포함되게 된다.
SELECT * FROM user_accounts ua
LEFT OUTER JOIN monthly_usage mu
ON ua.account_id = mu.account_id
WHERE NVL(mu.usage_status, '0') NOT IN ('3')
AND ua.usage_month = '2024-05'
주의사항
NVL
함수를 사용할 때는 해당 컬럼에 대한 인덱스 활용이 어려워질 수 있으므로, 성능 저하의 가능성을 고려해야 한다. 가능한 한 다른 방법을 우선적으로 고려하는 것이 좋다.
결론
NULL을 포함하는 어떤 연산도 그 결과는 NULL로 평가된다
SQL에서 NULL을 포함하는 어떤 연산(예: +, -, *, /)도 그 결과는 NULL로 평가된다. 예를 들어, NULL + 10의 결과는 NULL이다. 이는 SQL이 NULL을 '알 수 없는 값'으로 처리하며, 알 수 없는 값에 어떤 연산을 적용하더라도 결과 역시 '알 수 없음'이라고 판단하기 때문이다.NULL을 처리하는 방법
- IS NULL / IS NOT NULL: column_name IS NULL은 컬럼 값이 NULL일 때 true를 반환.
- NVL, COALESCE 함수: 이 함수들은 NULL 값을 다른 값으로 치환할 때 사용된다.