기록

[SQL] NULL이 포함된 연산은 NULL로 평가된다. 본문

제대로 이해하기

[SQL] NULL이 포함된 연산은 NULL로 평가된다.

youngyin 2024. 5. 14. 01:12

연산과 조건절에서의 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로 평가되지 않아 결과 집합에서 해당 행이 제외된다.

해결 방안

  1. 명시적 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'
  1. 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 값을 다른 값으로 치환할 때 사용된다.

'제대로 이해하기' 카테고리의 다른 글

Java/Labeled Loop  (0) 2024.04.26
[이해하기] DB/ANSI SQL, JOIN  (0) 2022.09.11
[이해하기] java/initialize block  (0) 2022.09.10
[이해하기] Android/API와 SDK  (0) 2022.05.24
[이해하기] kotlin/컬렉션  (0) 2022.03.27
Comments