본문으로 건너뛰기

NULL 속성

SQLD 목차데이터 모델과 SQL레슨 10 / 35

목차

NULL은 값이 없다는 표시입니다. 모델에서는 속성마다 NULL을 허용할지 정하고, SQL에서 NULL은 연산·비교·집계마다 정해진 방식으로 움직입니다.

정하늘은 아직 부서가 정해지지 않았고, 김민준은 맨 위라 관리자가 없습니다. 이런 칸에 0을 넣으면 '0번 부서'라는 틀린 사실이 되므로, 값이 없다는 사실을 NULL로 남깁니다. NULL은 다른 값과 다르게 움직여서, 규칙을 모르면 조건에서 행이 빠지거나 집계 함수(여러 행을 값 하나로 줄이는 함수 — GROUP BY와 집계 함수 레슨에서 자세히)의 평균이 달라집니다. 성과급 표 BONUS는 이 레슨에만 쓰는 테이블로, 이서연과 정하늘의 성과급 AMT가 NULL입니다.

6단계 중 1단계. NULL은 값이 없다는 표시로, 숫자 0이나 공백 문자와 다릅니다.

1NULL의 뜻과 모델의 NULL 허용

NULL은 잘못 들어간 값이 아니라 '아직 모름'이나 '해당 없음'을 적어 둔 것입니다. 숫자 0과 공백 한 칸 ' '은 값이 있으므로 NULL이 아닙니다. 다만 Oracle은 길이가 0인 빈 문자열 ''을 NULL로 다루고, SQL Server는 ''와 NULL을 구분합니다.

모델에서는 속성마다 NULL을 허용할지 정합니다.

속성NULL 허용까닭
EMPNO안 됨주식별자(존재성)
ENAME · SAL안 됨필수 속성
DEPTNO됨부서와 선택 관계의 FK
MGR됨관리자와 선택 관계의 FK

관계가 필수라면 FK도 NOT NULL입니다. NOT NULL인 칸에 NULL을 넣으려 할 때만 막힙니다.

2NULL의 연산과 비교

모르는 값에 무엇을 더해도 여전히 모르므로, NULL이 섞인 산술 연산(+ − × ÷)은 결과도 NULL입니다. NULL과 비교하면 참도 거짓도 아닌 '알 수 없음(UNKNOWN)'이 됩니다. WHERE 절은 조건이 참인 행만 남기므로 알 수 없음인 행도 빠집니다.

식결과WHERE에서
300 + NULLNULL—
NULL <> 20알 수 없음빠짐
NULL = NULL알 수 없음빠짐
NULL IS NULL참남음

= NULL은 오류 없이 0행을 내서 틀린 줄 모르고 지나치기 쉽습니다. NULL인지는 IS NULL·IS NOT NULL로 확인합니다. NOT IN(목록의 어느 값과도 같지 않으면 참 — WHERE 절 레슨에서 자세히)은 목록에 NULL이 있으면 'NULL과 같지 않다'가 늘 알 수 없음이라 어느 행도 남기지 않습니다.

3집계 함수와 NULL

집계 함수인 COUNT(개수)·SUM(합계)·AVG(평균)는 NULL인 칸을 건너뛰고 계산합니다. COUNT(*)만 칸이 아니라 행을 셉니다.

함수NULL인 칸을
COUNT(*)셈(행을 셈)
COUNT(열) · SUM(열)건너뜀
AVG(열)건너뛰고 남은 행 수로 나눔

그래서 성과급 평균은 6명이 아니라 4명으로 나눕니다. 성과급이 없는 사원까지 넣으려면 분모를 COUNT(*)로 씁니다.

예제

부서가 20이 아닌 사원을 찾으면 정하늘은 결과에 없습니다.

SELECT EMPNO,
  ENAME,
  DEPTNO
FROM EMP
WHERE DEPTNO <> 20
ORDER BY EMPNO;
EMPNOENAMEDEPTNO
1001김민준10
1004최유나30

= NULL로는 0행, IS NULL로는 정하늘 한 행이 나옵니다.

SELECT EMPNO,
  ENAME,
  DEPTNO
FROM EMP
WHERE DEPTNO = NULL;
SELECT EMPNO,
  ENAME,
  DEPTNO
FROM EMP
WHERE DEPTNO IS NULL;
EMPNOENAMEDEPTNO
1005정하늘NULL

목록에 NULL이 든 NOT IN은 한 행도 남기지 않습니다.

SELECT COUNT(*) AS CNT
FROM EMP
WHERE DEPTNO NOT IN
    (10, NULL);
CNT
0

성과급 표 BONUS를 만들어 월급과 더하면, 성과급이 NULL인 두 사원은 합계 TOTAL도 NULL입니다.

CREATE TABLE BONUS (
  EMPNO NUMBER PRIMARY KEY
    REFERENCES EMP (EMPNO),
  AMT NUMBER
);
INSERT INTO BONUS
VALUES (1001, 60);
INSERT INTO BONUS
VALUES (1002, NULL);
INSERT INTO BONUS
VALUES (1003, 30);
INSERT INTO BONUS
VALUES (1004, 40);
INSERT INTO BONUS
VALUES (1005, NULL);
INSERT INTO BONUS
VALUES (1006, 50);
SELECT E.ENAME,
  E.SAL,
  B.AMT,
  E.SAL + B.AMT AS TOTAL
FROM EMP E
  INNER JOIN BONUS B
  ON B.EMPNO = E.EMPNO
ORDER BY E.EMPNO;
ENAMESALAMTTOTAL
김민준52060580
이서연380NULLNULL
박지호41030440
최유나45040490
정하늘300NULLNULL
강도윤47050520

TOTAL을 모두 더하는 SUM(E.SAL + B.AMT)는 NULL인 두 행이 통째로 빠져 2030입니다. 열마다 따로 더하는 SUM(E.SAL) + SUM(B.AMT)⁠는 NULL 칸만 빼므로 2530 + 180 = 2710입니다.

같은 표로 집계합니다. AVG(AMT)와 SUM(AMT) / COUNT(*)를 견줘 보세요.

CREATE TABLE BONUS (
  EMPNO NUMBER PRIMARY KEY
    REFERENCES EMP (EMPNO),
  AMT NUMBER
);
INSERT INTO BONUS
VALUES (1001, 60);
INSERT INTO BONUS
VALUES (1002, NULL);
INSERT INTO BONUS
VALUES (1003, 30);
INSERT INTO BONUS
VALUES (1004, 40);
INSERT INTO BONUS
VALUES (1005, NULL);
INSERT INTO BONUS
VALUES (1006, 50);
SELECT COUNT(*),
  COUNT(AMT),
  SUM(AMT),
  AVG(AMT),
  SUM(AMT) / COUNT(*)
    AS PER_ROW
FROM BONUS;
COUNT(*)COUNT(AMT)SUM(AMT)AVG(AMT)PER_ROW
641804530

시험 포인트

  • NULL은 0이나 공백 한 칸과 다릅니다. Oracle은 빈 문자열 ''을 NULL로 다룹니다.
  • 'FK는 NULL이 될 수 없다'는 보기는 틀립니다. 선택 관계의 FK는 NULL을 허용합니다.
  • NULL이 섞인 산술은 NULL, 비교는 알 수 없음이라 WHERE에서 빠집니다. = NULL은 오류 없이 0행이고, NULL은 IS NULL로 찾습니다.
  • NOT IN 목록에 NULL이 있으면 0행입니다.
  • COUNT(*) 말고는 집계 함수가 NULL을 건너뜁니다. AVG(열)과 SUM(열) / COUNT(*)는 다를 수 있습니다.
  • NULL이 있으면 SUM(열1 + 열2)와 SUM(열1) + SUM(열2)가 다를 수 있습니다.

확인 문제

1. 다음 쿼리의 결과 두 값은 무엇일까요?

SELECT COUNT(*) AS CNT,
  SUM(SAL) AS SUM_SAL
FROM EMP
WHERE MGR <> 1001;
정답

3과 1090입니다.

CNTSUM_SAL
31090

최유나·강도윤은 MGR이 1001이라 거짓, 김민준은 MGR이 NULL이라 알 수 없음이 되어 세 사람 모두 빠집니다. 정하늘은 부서가 NULL이어도 MGR이 1004라 남아, 이서연·박지호와 함께 380 + 410 + 300 = 1090입니다. 김민준을 넣어 4와 1610으로 셈하면 틀립니다.

2. 도서 대출 엔터티의 속성 대출번호(주식별자) · 회원번호(빌린 회원) · 대출일 · 반납일 가운데 NULL을 허용할 속성은 무엇이고, 까닭은 무엇일까요?

정답

반납일입니다. 아직 돌려받지 않은 대출은 반납일이 없으므로 비워 둡니다. 아무 날짜나 넣으면 이미 반납했다는 틀린 사실이 됩니다. 나머지는 주식별자, 대출마다 꼭 있는 회원(필수 관계의 FK), 빌릴 때 늘 생기는 날짜라 NOT NULL입니다.

3. EMP의 ENAME은 NOT NULL입니다. Oracle에서 다음 INSERT는 들어갈까요? 까닭도 말해 보세요.

INSERT INTO EMP
  (EMPNO, ENAME, SAL)
VALUES (1007, '', 400);
정답

들어가지 않습니다. Oracle은 빈 문자열 ''을 NULL로 다루므로, NOT NULL인 ENAME에 NULL을 넣으려 한 것이 되어 막힙니다(ORA-01400).

끝까지 내려오면 저절로 표시됩니다. 이 기기에만 저장합니다.

SQLD 목차로