NULL의 함정

개발새발log·2023년 4월 28일
0

쉬운 코드 님의 영상을 보고 정리한 글 입니다

SQL에서 NULL의 의미

1) unknown
2) unavailable/withheld
3) not applicable

어떤 값이 null로 처리된 데는 다양한 맥락이 있을 수 있다는 전제를 깔고 가야 한다.

사용자가 생일을 입력을 안 했으니 생일이 없다는 말이 아니고, "생일이 언제인지 모른다"로 보는 게 맞다. SQL은 이런 모호함을 고려하여 null을 설계했다는 점을 먼저 인지하고 넘어가야 한다.

이런 맥락에서인지, 보통 null 연산은 value = NULL/value != NULL이 아니라, value IS NULL/value IS NOT NULL 이렇게 표현한다.

NULL과 three-valued logic

null까지 고려하면 주의할 점이 있다 :

👉 비교 연산 시, UNKNOWN으로 처리될 수 있다는 점이다 (UNKNOWN: 모른다. TRUE일 수도, FALSE일 수도 있다)

즉, 세가지 상태가 존재한다:

- TRUE
- FALSE
- UNKNOWN

AND, OR, NOT 연산

UNKNOWN이 포함된 경우를 따져보자:

✔️ AND

- TRUE AND UNKNOWN -> UNKNOWN
- FALSE AND UNKNOWN -> FALSE
- UNKNOWN AND UNKNOWN -> UNKNOWN

✔️ OR

- TRUE OR UNKNOWN -> TRUE
- FALSE OR UNKNOWN -> FALSE
- UNKNOWN OR UNKNOWN -> UNKNOWN

✔️ NOT

- NOT TRUE -> FALSE
- NOT FALSE -> TRUE
- NOT UNKNOWN -> UNKNOWN

SQL의 three valued logic이 중요한 이유는, 우리가 쿼리를 작성할 때 WHERE절은 TRUE인 것에 대해서만 선택한다는 점 때문이다. 만약 null이 포함된 데이터가 존재하면 결과가 의도치않게 FALSE이거나 UNKNOWN이 나올 수 있다.

예시: not in 사용시

쉬운 코드 님의 예시를 차용 했습니다

2000년대생이 없는 부서의 ID와 이름 찾기

SELECT D.id, D.name
FROM department AS D
WHERE D.id NOT IN (
				SELECT E.dept_id
            	FROM employee E
            	WHERE E.birth_date >= '2000-01-01'
			);

E.dept_id에 null이 포함되어 있다면 어떻게 동작할까 ?

null >= '2000-01-01'의 결과는 unknown이다. 결국 WHERE 절은 v not in (..., unknown, ...) 과 같이 동작할 것이다.

v not in (v1, v2, ...)v != v1 AND v != v2 ...와 같기 때문에, unknown이 하나라도 포함됐을 때 아래와 같은 경우가 있을 수 있다:

3 not in (1, 3, NULL) => FALSE
3 not in (1, 2, NULL) => UNKNOWN

이런 케이스에 따라, E.dept_id이 null일 때 우리의 의도와 다르게 조건문의 결과가 FALSE, UNKNOWN이 나와서 데이터가 아예 표출되지 않을 수도 있다.

null을 고려해서 쿼리를 짠다면, 아래와 같이 개선할 수 있다:

1) null 조건 추가

SELECT D.id, D.name
FROM department AS D
WHERE D.id NOT IN (
				SELECT E.dept_id
            	FROM employee E
            	WHERE E.birth_date >= '2000-01-01' AND E.dept_id IS NOT NULL 
			);

2) NOT EXISTS 활용

SELECT D.id, D.name
FROM department AS D
WHERE NOT EXISTS (
				SELECT *
            	FROM employee E
            	WHERE E.depth_id = D.id AND E.birth_date >= '2000-01-01'
			);

📍 정리하자면, 데이터가 null 허용일 때 이를 고려해서 방어적으로 쿼리를 짤 필요가 있다.

🫠 반.모 (aka. 반성 모드)

덜덜 전에는 이걸 몰라서 전혀 고려하지 못하고 사용한 듯 ,, 고백하자면 이 글은 영상 보다가 등골이 서늘해서 황급히 정리한 글 입니다 ..

profile
⚠️ 주인장의 머릿속을 닮아 두서 없음 주의 ⚠️

0개의 댓글