SQL - NULL 총정리
SQL에서 NULL 값 처리 정리
NULL의 기본 개념
NULL은 "값이 없음" 또는 "알 수 없는 값"을 나타내는 특별한 상태이다. 중요한 점은 NULL은 0도 아니고 빈 문자열('')도 아니라는 것.
- 0: 숫자 0이라는 명확한 값
- 빈 문자열(''): 길이가 0인 문자열이라는 명확한 값
- NULL: 값 자체가 존재하지 않는 상태
NULL과 비교 연산
SQL에서 NULL은 삼중 논리(Three-Valued Logic)를 따른다. 일반적인 프로그래밍 언어의 TRUE/FALSE와 달리, SQL은 TRUE, FALSE, UNKNOWN 세 가지 값을 가진다.
-- 잘못된 비교
SELECT * FROM users WHERE email = NULL; -- 결과 없음
SELECT * FROM users WHERE email != NULL; -- 결과 없음
-- 올바른 비교
SELECT * FROM users WHERE email IS NULL; -- NULL인 행 반환
SELECT * FROM users WHERE email IS NOT NULL; -- NULL이 아닌 행 반환
NULL과의 모든 비교 연산은 UNKNOWN을 반환합니다. 이는 WHERE 절에서 조건이 TRUE인 행만 선택하기 때문에, NULL 비교 시 IS NULL 또는 IS NOT NULL을 사용해야 한다.
-- NULL과의 비교 연산 결과
NULL = 10 -- UNKNOWN
NULL != 10 -- UNKNOWN
NULL > 10 -- UNKNOWN
NULL AND TRUE -- UNKNOWN
NULL OR FALSE -- UNKNOWN
GROUP BY와 NULL
NULL은 하나의 그룹으로 묶인다
GROUP BY 절에서 NULL 값이 있는 컬럼을 기준으로 그룹화하면, 모든 NULL 값들이 하나의 그룹으로 묶인다.
예제 데이터
| id | region | sales_amount |
|---|---|---|
| 1 | East | 100 |
| 2 | West | 150 |
| 3 | NULL | 200 |
| 4 | East | 250 |
| 5 | NULL | 300 |
쿼리 및 결과
SELECT region, SUM(sales_amount) AS total_sales
FROM sales
GROUP BY region;
결과:
| region | total_sales |
|---|---|
| East | 350 |
| West | 150 |
| NULL | 500 |
NULL 값을 가진 행(id=3, id=5)이 하나의 그룹으로 묶여서 합계 500이 계산된다.
NULL 그룹 제외하기
NULL 그룹을 제외하고 싶다면 WHERE 절이나 HAVING 절을 사용하면 된다.
-- WHERE 절로 NULL 제외 (그룹화 전에 제외)
SELECT region, SUM(sales_amount) AS total_sales
FROM sales
WHERE region IS NOT NULL
GROUP BY region;
-- HAVING 절로 NULL 제외 (그룹화 후에 제외)
SELECT region, SUM(sales_amount) AS total_sales
FROM sales
GROUP BY region
HAVING region IS NOT NULL;
NULL을 다른 값으로 변환하기
NULL을 특정 값으로 변환하여 그룹화할 수도 있다.
-- COALESCE 사용
SELECT COALESCE(region, 'Unknown') AS region,
SUM(sales_amount) AS total_sales
FROM sales
GROUP BY COALESCE(region, 'Unknown');
-- MySQL: IFNULL
SELECT IFNULL(region, 'Unknown') AS region,
SUM(sales_amount) AS total_sales
FROM sales
GROUP BY IFNULL(region, 'Unknown');
-- Oracle: NVL
SELECT NVL(region, 'Unknown') AS region,
SUM(sales_amount) AS total_sales
FROM sales
GROUP BY NVL(region, 'Unknown');
집계함수와 NULL 처리
모든 집계함수는 기본적으로 NULL 값을 무시한다. 단, COUNT(*)는 예외
COUNT 함수
COUNT 함수는 사용 방식에 따라 NULL 처리가 다르다.
COUNT(*) vs COUNT(column)
-- 예제 데이터
-- id | name | score
-- 1 | Alice | 90
-- 2 | Bob | 80
-- 3 | Carol | NULL
-- 4 | Dave | 70
-- COUNT(*): 전체 행 개수 (NULL 포함)
SELECT COUNT(*) FROM students;
-- 결과: 4
-- COUNT(column): NULL이 아닌 값의 개수
SELECT COUNT(score) FROM students;
-- 결과: 3
-- COUNT(1): COUNT(*)와 동일 (NULL 포함)
SELECT COUNT(1) FROM students;
-- 결과: 4
정리:
- COUNT(*): 모든 행을 세며, NULL 포함
- COUNT(column): 해당 컬럼에서 NULL이 아닌 값만 셈
- COUNT(1): COUNT(*)와 동일하게 동작 (성능도 동일)
SUM 함수
SUM 함수는 NULL 값을 무시하고 나머지 숫자만 합산한다.
-- 예제 데이터
-- id | amount
-- 1 | 100
-- 2 | 200
-- 3 | NULL
-- 4 | 300
SELECT SUM(amount) FROM orders;
-- 결과: 600 (100 + 200 + 300)
-- NULL은 무시됨, 0으로 계산되지 않음
중요: NULL + 숫자 = NULL이지만, SUM 함수는 내부적으로 NULL을 제외하고 계산한다.
-- 직접 연산 vs SUM 함수
SELECT 100 + 200 + NULL + 300; -- 결과: NULL
SELECT SUM(amount) FROM orders; -- 결과: 600
AVG 함수
AVG 함수는 NULL을 제외한 값들의 평균을 계산한다. 이 부분에서 주의가 필요하다.
-- 예제 데이터
-- id | score
-- 1 | 10
-- 2 | 20
-- 3 | 30
-- 4 | NULL
-- AVG 함수: NULL 제외
SELECT AVG(score) FROM students;
-- 결과: 20 = (10 + 20 + 30) / 3
-- NULL을 0으로 처리하고 싶다면
SELECT AVG(IFNULL(score, 0)) FROM students; -- MySQL
-- 결과: 15 = (10 + 20 + 30 + 0) / 4
-- 또는 직접 계산
SELECT SUM(score) / COUNT(*) FROM students;
-- 결과: 15 = 60 / 4
정리:
- AVG(column): NULL을 제외하고 평균 계산 = SUM(column) / COUNT(column)
- AVG(IFNULL(column, 0)): NULL을 0으로 치환하여 평균 = SUM(column) / COUNT(*)
MIN / MAX 함수
MIN과 MAX 함수는 NULL을 무시하고 최소값/최대값을 반환한다.
-- 예제 데이터
-- id | price
-- 1 | 100
-- 2 | NULL
-- 3 | 300
-- 4 | 200
SELECT MIN(price), MAX(price) FROM products;
-- 결과: MIN = 100, MAX = 300
-- NULL은 무시됨