데이터베이스 Null
IT 위키
더 많은 작업
- Null
- 값이 없거나 알 수 없음을 나타내는 특별한 표시. 0이나 공백 문자와는 다르다.
관계형 데이터 모델에서 Null은 아래 3가지 중 하나를 의미한다.
- Value is unknown
- 값을 모름
- Value exists but is not available
- 값이 존재하지만 사용할 수 없음
- Attribute does not apply to this tuple (also known as value undefined)
- 속성 자체가 이 튜플에 해당사항이 없음
- 인사 정보 테이블에서 "학교"라는 필드가 Null인 경우
- 출신 학교가 어딘지 몰라서 비워둔 경우
- 출신 학교는 대학교만 적을 수 있는데 고졸 학력이라 적을 수 없는 경우
- 직원 가족 정보도 직원 테이블에 함께 보관하는데, 막 태어난 신생아라 학교가 해당사항이 없는 경우
- 퇴직 직원이라 학교 정보를 보관할 수 없는 경우
- 등등 여러가지 상황을 고려할 수 있다.
아래 예는 모두 오라클에서 실행한 결과다. 예제 테이블은 다음과 같다.
사원 테이블
| 사번 | 이름 | 급여 | 수당 |
|---|---|---|---|
| 1 | 김 | 300 | 50 |
| 2 | 이 | 200 | NULL |
| 3 | 박 | 400 | 100 |
| 4 | 최 | 100 | NULL |
| 5 | 정 | 500 | 0 |
NULL이 포함된 산술 연산의 결과는 NULL이다. 0이 아니다.
SELECT 사번, 급여, 수당, 급여 + 수당 AS 합계 FROM 사원 ORDER BY 사번;
| 사번 | 급여 | 수당 | 합계 |
|---|---|---|---|
| 1 | 300 | 50 | 350 |
| 2 | 200 | NULL | NULL |
| 3 | 400 | 100 | 500 |
| 4 | 100 | NULL | NULL |
| 5 | 500 | 0 | 500 |
- NULL을 0으로 보고 계산하려면
급여 + NVL(수당, 0)처럼 NVL로 바꾼다. - 오라클의 문자열 연결 연산자는 예외다.
NULL || 'A'의 결과는 NULL이 아니라 'A'다(NULL을 빈 문자열처럼 다룬다).
NULL과 비교하면 결과는 참(TRUE)도 거짓(FALSE)도 아닌 UNKNOWN(알 수 없음)이다. WHERE 절은 조건이 TRUE인 행만 돌려주므로, UNKNOWN인 행은 결과에서 빠진다. 그래서 SQL은 TRUE, FALSE, UNKNOWN의 3값 논리를 쓴다. 아래 표는 오라클에서 CASE 식으로 각 조합을 확인한 결과다(T = TRUE, F = FALSE, U = UNKNOWN).
| A | B | A AND B | A OR B |
|---|---|---|---|
| T | U | U | T |
| F | U | F | U |
| U | U | U | U |
| A | NOT A |
|---|---|
| U | U |
- AND는 한쪽이 FALSE이면 다른 쪽과 관계없이 FALSE, OR는 한쪽이 TRUE이면 다른 쪽과 관계없이 TRUE다. 그 밖에 UNKNOWN이 섞이면 UNKNOWN이다.
SELECT COUNT(*) AS CNT FROM 사원 WHERE 수당 = NULL; -- 결과 0
SELECT 사번 FROM 사원 WHERE 수당 IS NULL ORDER BY 사번; -- 결과 2, 4
SELECT 사번 FROM 사원 WHERE 수당 <> 50 ORDER BY 사번; -- 결과 3, 5
수당 = NULL은 수당이 NULL인 행에서도 UNKNOWN이 되므로 아무 행도 찾지 못한다. 오류가 나지 않고 0행이 나오기 때문에 실수를 알아채기 어렵다. NULL 여부는 반드시IS NULL,IS NOT NULL로 확인한다.수당 <> 50도 NULL인 2번, 4번 사원을 돌려주지 않는다. NULL은 50과 같은지 다른지 알 수 없기 때문이다.
서브쿼리 결과에 NULL이 하나라도 섞이면 NOT IN 조건은 어떤 행에서도 TRUE가 될 수 없다. 사번 NOT IN (1, NULL)은 사번 <> 1 AND 사번 <> NULL과 같고, 뒤쪽이 항상 UNKNOWN이기 때문이다.
퇴직 테이블
| 사번 |
|---|
| 1 |
| NULL |
SELECT 사번 FROM 사원 WHERE 사번 NOT IN (SELECT 사번 FROM 퇴직) ORDER BY 사번;
-- 0행
SELECT 사번 FROM 사원 WHERE 사번 NOT IN (SELECT 사번 FROM 퇴직 WHERE 사번 IS NOT NULL) ORDER BY 사번;
-- 2, 3, 4, 5
SELECT 사번 FROM 사원 s
WHERE NOT EXISTS (SELECT 1 FROM 퇴직 t WHERE t.사번 = s.사번) ORDER BY 사번;
-- 2, 3, 4, 5
- 반면 IN은 영향을 받지 않는다.
사번 IN (SELECT 사번 FROM 퇴직)은 1번 사원 1행을 돌려준다. OR 조건 중 하나가 TRUE이면 되기 때문이다. - NOT EXISTS는 NULL에 영향을 받지 않으므로, 서브쿼리 칼럼에 NULL이 있을 수 있다면 NOT EXISTS를 쓰거나 서브쿼리에서 NULL을 걸러낸다.
COUNT(*)를 제외한 집계 함수는 NULL을 무시하고 계산한다. 자세한 내용은 SQL 그룹 함수 참고.
SELECT COUNT(*) AS CNT_ALL, COUNT(수당) AS CNT_수당, SUM(수당) AS SUM_수당,
AVG(수당) AS AVG_수당, AVG(NVL(수당, 0)) AS AVG_NVL
FROM 사원;
| CNT_ALL | CNT_수당 | SUM_수당 | AVG_수당 | AVG_NVL |
|---|---|---|---|---|
| 5 | 3 | 150 | 50 | 30 |
- COUNT(*)는 행 수 5, COUNT(수당)은 NULL이 아닌 값의 수 3이다. 0은 NULL이 아니므로 센다.
- AVG(수당)은 150 / 3 = 50이다. 분모에서 NULL 행이 빠진다. NULL을 0으로 보고 전체 인원으로 나누려면 AVG(NVL(수당, 0)) = 150 / 5 = 30처럼 써야 한다.
| 구분 | 오라클 | SQL Server |
|---|---|---|
| NULL 취급 | 가장 큰 값 | 가장 작은 값 |
| ORDER BY ... ASC | NULL이 맨 뒤 | NULL이 맨 앞 |
| ORDER BY ... DESC | NULL이 맨 앞 | NULL이 맨 뒤 |
| 위치 직접 지정 | NULLS FIRST / NULLS LAST | 지원하지 않음(CASE 식으로 정렬 키를 따로 만든다) |
SELECT 사번, 수당 FROM 사원 ORDER BY 수당, 사번; -- (5, 0) (1, 50) (3, 100) (2, NULL) (4, NULL)
SELECT 사번, 수당 FROM 사원 ORDER BY 수당 DESC, 사번; -- (2, NULL) (4, NULL) (3, 100) (1, 50) (5, 0)
SELECT 사번, 수당 FROM 사원 ORDER BY 수당 NULLS FIRST, 사번; -- (2, NULL) (4, NULL) (5, 0) (1, 50) (3, 100)
오라클은 길이가 0인 문자열 ''을 NULL로 취급한다. 따라서 ''을 넣은 칼럼은 IS NULL로 찾아야 하며, = ''로는 찾을 수 없다. SQL Server, PostgreSQL 등은 빈 문자열과 NULL을 서로 다른 값으로 구분한다.
SELECT CASE WHEN '' IS NULL THEN 'NULL이다' ELSE 'NULL 아님' END AS 결과,
LENGTH('') AS 길이
FROM DUAL;
| 결과 | 길이 |
|---|---|
| NULL이다 | NULL |
| 함수 | 동작 | DBMS |
|---|---|---|
| NVL(a, b) | a가 NULL이면 b, 아니면 a | 오라클 |
| NVL2(a, b, c) | a가 NULL이 아니면 b, NULL이면 c | 오라클 |
| NULLIF(a, b) | a와 b가 같으면 NULL, 다르면 a | 표준(오라클·SQL Server 공통) |
| COALESCE(a, b, …) | 인수 중 NULL이 아닌 첫 번째 값. 모두 NULL이면 NULL | 표준(오라클·SQL Server 공통) |
| ISNULL(a, b) | a가 NULL이면 b, 아니면 a(오라클의 NVL과 같은 역할) | SQL Server |
SELECT 사번, 수당,
NVL(수당, 0) AS NVL_,
NVL2(수당, '있음', '없음') AS NVL2_,
NULLIF(수당, 0) AS NULLIF_,
COALESCE(수당, 급여, 0) AS COALESCE_
FROM 사원 ORDER BY 사번;
| 사번 | 수당 | NVL_ | NVL2_ | NULLIF_ | COALESCE_ |
|---|---|---|---|---|---|
| 1 | 50 | 50 | 있음 | 50 | 50 |
| 2 | NULL | 0 | 없음 | NULL | 200 |
| 3 | 100 | 100 | 있음 | 100 | 100 |
| 4 | NULL | 0 | 없음 | NULL | 100 |
| 5 | 0 | 0 | 있음 | NULL | 0 |
- 5번 사원은 수당이 0이므로 NVL2는 '있음'이고, NULLIF(0, 0)은 NULL이 된다.
- NULLIF는 0으로 나누기를 피할 때 자주 쓴다.
a / NULLIF(b, 0)은 b가 0이면 오류 대신 NULL을 돌려준다.
- 필수 속성과 선택 속성: 모든 인스턴스가 반드시 값을 가져야 하는 속성은 필수(Mandatory), 값이 없을 수 있는 속성은 선택(Optional) 속성이다. 필수 속성은 물리 모델에서
NOT NULL제약으로 구현한다. - 주식별자는 NULL일 수 없다: PK 칼럼은 자동으로 NOT NULL이 된다(데이터베이스 개체 무결성). 인조식별자를 PK로 쓰면 PK에는 NULL이 생기지 않지만, 정작 업무상 식별에 쓰는 본질 속성(이메일, 주민등록번호 등)에 NULL이 들어갈 수 있다. 본질 속성에도 NOT NULL과 UNIQUE를 함께 걸지 판단해야 한다. 데이터베이스 식별자 참고.
- FK와 NULL: 비식별 관계의 FK 칼럼이 NULL을 허용하면 부모 없이도 자식 행이 존재할 수 있다(선택 관계).
- 기본값(DEFAULT) 설정: 수당·할인액처럼 '없음'이 사실상 0인 속성을 NULL로 두면 합계·평균 계산에서 NULL 처리가 필요하고, 위의 AVG 예처럼 결과가 의도와 달라질 수 있다. 이런 속성은 DEFAULT 0과 NOT NULL을 함께 지정하는 것을 검토한다. 반대로 '모름'과 '0'을 구분해야 하는 속성(예: 측정하지 않은 시험 점수)은 NULL을 허용하는 것이 맞다.
CREATE TABLE 주문 (
주문번호 NUMBER PRIMARY KEY,
할인액 NUMBER DEFAULT 0 NOT NULL
);
INSERT INTO 주문 (주문번호) VALUES (1); -- 할인액에 0이 들어간다
INSERT INTO 주문 VALUES (2, NULL); -- ORA-01400: NULL을 넣을 수 없음
SELECT * FROM 주문; -- (1, 0) 1행
- DEFAULT는 값을 생략했을 때만 적용된다. NULL을 명시적으로 넣으면 DEFAULT가 적용되지 않으므로 NOT NULL을 함께 걸어야 막을 수 있다.
- NULL과의 산술 연산 결과는 NULL, 비교 연산 결과는 UNKNOWN이며 WHERE 절에서 걸러진다.
= NULL은 항상 0행이다. - NOT IN 서브쿼리 결과에 NULL이 있으면 0행이 나온다. IN은 영향이 없다.
- COUNT(*)와 COUNT(칼럼)의 차이, AVG의 분모에서 NULL 행이 빠지는 것을 이용한 결과값 계산 문제
- 오라클은 NULL을 가장 큰 값으로 보아 ASC 정렬 시 맨 뒤, SQL Server는 맨 앞에 둔다.
- NVL, NVL2, NULLIF, COALESCE의 결과값 계산과 오라클에서
''이 NULL로 취급되는 점
- Fundamentals of Database Systems, Elmasri, Navathe
- 지식잡식 - [데이터베이스] Null값이 의미하는 두 가지
- Codd, E. F. (1979). "Extending the Database Relational Model to Capture More Meaning". ACM Transactions on Database Systems, 4(4), 397–434. doi:10.1145/320107.320109
- Oracle Database SQL Language Reference – Nulls
- Oracle Database SQL Language Reference – SELECT (order_by_clause, NULLS FIRST/LAST)
- Microsoft Learn – NULL and UNKNOWN (Transact-SQL)
- Microsoft Learn – SELECT – ORDER BY Clause (Transact-SQL)