본문으로 이동
메뉴 여닫기
환경 설정 메뉴 여닫기
개인 메뉴 여닫기
로그인하지 않음
지금 편집한다면 당신의 IP 주소가 공개될 수 있습니다.
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인 경우
    • 출신 학교가 어딘지 몰라서 비워둔 경우
    • 출신 학교는 대학교만 적을 수 있는데 고졸 학력이라 적을 수 없는 경우
    • 직원 가족 정보도 직원 테이블에 함께 보관하는데, 막 태어난 신생아라 학교가 해당사항이 없는 경우
    • 퇴직 직원이라 학교 정보를 보관할 수 없는 경우
  • 등등 여러가지 상황을 고려할 수 있다.

SQL에서 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을 빈 문자열처럼 다룬다).

비교 연산과 3값 논리

편집 원본 편집

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이다.

IS NULL과 IS NOT NULL, = NULL

편집 원본 편집
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처럼 써야 한다.

정렬 시 NULL의 위치

편집 원본 편집
구분 오라클 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

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을 돌려준다.

모델링에서의 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로 취급되는 점