IT's Jenna

7. NULL과의 싸움 본문

Study/관계형 데이터 베이스 실전 입문

7. NULL과의 싸움

developer Jenna 2021. 6. 1. 14:23
NULL이란?
NULL을 사용하면 안 되는 이유
NULL의 대책

1. NULL이란?

칼럼의 값을 모를 때 사용하는 표식으로, 값이 존재하지 않거나 분명하지 않음을 나타낸다.

흔히들 NULL값이라고하지만, 이것은 잘못된 표현이다. NULL이 들어있는 속성 값은 존재하지 않는 것이며 NULL은 값이 없다! NULL값이 아니라 단순히 NULL이라고 표현하도록 하자.

2. NULL을 사용하면 안되는 이유

a. 연산 불가

NULL은 연산자로 비교할 수 없다. 예를 들어 table.attribute == NULL 연산은 불가하다. SQL에서 NULL인지 아닌지에 대한 판단은 IS NULL 또는 IS NOT NULL로 가능하다.

 

일반적으로 쿼리를 이용해서 칼럼의 값을 비교하거나 쿼리 연산으로 값을 가공하는데, NULL이 있으면 이러한 비교 연산에 문제를 일으킨다. 예를 들어 NULL + 1의 값은 NULL이다. 알 수 없는 값에 어떤 수를 더해도 답은 알 수 없다. 

 

문자열도 마찬가지이다. 아래 예시를 보자. 여기서 CONCAT은 여러 데이터들을 이어 붙여서 하나의 값으로 만드는 쿼리이다. 

CONCAT('ABC', NULL) 
  • ABC NULL 이 아니라 그냥 NULL이라는 결과를 반환한다.
NULL > 100
  • true, false가 아닌 NULL을 반환한다.

 

b. 의도하지 않은 검색 결과의 반환

테이블에서 SELECT를 이용하여 데이터를 받아올 때도 NULL이 있으면 반환 값에 오류가 생길 수 있다. 예를 들어 나이가 20살이 아닌 모든 사용자를 받아오는 쿼리를 작성해보자.

SELECT * FROM users WHERE age <> 20

Table. users

의도한 결과값은 규리, 상민, 동균이 나오는것이다. 하지만 실제 결과값은 다음과 같다. 

이런 경우에 의도한 결과값을 얻기 위해서는 다음과 같이 쿼리를 작성해주어야 한다.

SELECT * FROM users WHERE age <> 20 OR age IS NULL;

 

c. NULL에 의한 3치 논리

SELECT 결과가 반환될 때는 WHERE절의 조건이 TRUE가 될 때뿐이다. FALSE 또는 NULL의 경우는 결과를 반환하지 않는다. 실제로 NULL은 논리 값이 아니지만 이렇게 True, False, Unkown 이라는 세 가지 논리 값에 의해서 판정이 되는 경우를 3치 논리(3VL)라고 한다.

<3치논리 논리표>

논리표만 봐도 TRUE/FALSE만 있는 2VL보다 훨씬 복잡한 것을 알 수 있다. 하지만 논리적으로 잘못된 것은 없다. 다만 3VL이 문제가 되는 이유는 현실을 제대로 표현할 수 없기 때문이다. 사실상 3VL이 논리적으로 맞더라도 결과값 자체는 정확하지 않기 때문에 결국은 의미 없는 데이터가 되는 것이다.

 

d. 관계형 모델 파괴

관계형 모델은 릴레이션의 연산으로 모든 질의가 해결되어야 한다. 릴레이션에 NULL이 존재하면 릴레이션 간 연산을 할 때 3VL 논리식에 따라 기계적인 연산은 할 수 있으나 그 값이 현실적으로나 의미적으로 올바른지는 알 수 없다. 결과적으로 관계형 모델을 파괴하게 된다.

 

e. 옵티마이저에 대한 폐해

SQL 개발자가 쿼리를 작성하고 실행할 때 옵티마이저는 sql을 어떻게 실행할지 계획해주는 소프트웨어이다. 동일한 결과가 나오는 쿼리문이라도 쿼리를 어떻게 실행하느냐에 따라서 성능이 달라지고, 그에 따른 예상 비용도 달라진다.

그런데 테이블에 NULL이 존재하면 옵티마이저가 수학적으로 증명할 수 있는 쿼리의 조합이 제한된다. 즉 최적의 성능을 낼 수 있는 쿼리의 조합을 찾지 못하는 것이다. 자연스럽게 쿼리의 비용 계산에도 악영향을 끼칠 수밖에 없다.

 

3. NULL의 대책

지금까지 NULL을 사용하면 발생할 수 있는 문제점들을 알아보았다. 하지만 실무에선 테이블에 어쩔 수 없이 NULL이 포함되어야 하는 경우가 많은데 이럴 땐 어떻게 해야 할까?

 

a. 정규화

NULL을 제거하는 가장 전통적인 방법은 테이블을 정규화하는 것이다. 일반적으로 테이블에 NULL이 포함되는 경우는 애플리케이션엔 필요 없는 데이터가 테이블엔 있는 경우일 수 있다. 아래 예제에서 학생들의 학년은 필수적으로 정해져 있지만 동아리는 그렇지 않다. 이런 경우에는 애플리케이션의 기능이 다른 것처럼 테이블도 나누어서 작성해야 한다.

b. 잘못된 해결 방법

흔히 하는 실수 중에 NULL 대신 특정한 값을 넣는 경우가 있다. 예를 들어 위의 users 테이블에 나이를 모르면 NULL이 아니라 -1을 넣는다고 가정해보자. 이런 경우 OR age IS NULL을 추가하지 않더라도 20살이 아닌 사람들의 데이터를 불러올 수 있다. 하지만 20살 이하인 사람들을 불러오고 싶다면 어떻게 될까? age에 -1을 가지고 있는 사람들의 데이터도 모두 불러오게 될 것이다. 하지만 이들이 정말 20살 이하라고 확신할 수 있을까? 결국 NULL대신 특정값을 default로 넣어두는 것은 경우에 따라 더 큰 문제를 야기시킬 수 있다는 것을 유의하자.

 

c. COALESCE 함수

테이블 자체에 NULL을 없애더라도 연산 과정에서 NULL이 발생할 수 있다. 이럴 때 사용할 수 있는 COALESCE 함수가 있다. COALESCE 함수는 인수 중에 가장 처음에 나오는 NULL이 아닌 값을 반환한다. 예를 들어 다음과 같은 쿼리문을 작성했을 때 population의 SUM이 NULL이라면 0을 반환하고, NULL이 아니면 처음 값을 반환하다. 따라서 해당 함수를 사용하면 연산 값이 NULL일 때 default 값을 넣어주기 용이하다.

Table. countries

SELECT continent, COALESCE(SUM(population),0) FROM sys.countries GROUP BY continent;

  • 인수가 NULL이 아닌 값이 있는 경우 그 첫 번째 값을 반환한다.
SELECT continent, COALESCE(SUM(nulltest),0) FROM sys.countries GROUP BY continent;

  • 첫번째 인수의 결과가 NULL이기 때문에 다음 인수인 0을 반환한다.

 

d. 빈 문자열의 처리

특정 소프트웨어에서는 길이가 0인 문자열과 NULL을 같은 의미로 취급하는 경우가 있다. 이런 경우엔 한 개의 공백 문자를 회피책으로 사용할 수 있다.


이번장에서는 NULL이란 무엇인지 그리고 관계형 모델에 NULL이 있을 때 생길 수 있는 문제점과 해결책을 알아보았다. 그런데 SQL에서는 NULL의 사용이 허용되는 경우가 있다. 다음장부터는 SQL에서 관계형 모델이 아닌 데이터를 어떻게 처리하는지 알아보도록 하자.

 

Comments