관계형 모델에서 NULL이란

L-cloud·2023년 2월 14일
0
post-thumbnail
  • 본 글에서는 관계형 데이터에서의 NULL을 이야기 한다. 여기서의 관계형 모델에서의 NULL로 한정한다.

    목차
    1. 관계형 모델이란 무엇인가
    2. NULL이란 무엇인가?
    3. NULL을 제거할 수 있는 방법은 무엇이 있을까?

관계형 모델이란 무엇인가

관계형 모델은 데이터를 표현하는 방식 중 하나이다. KVS(Kye-value-store)처럼 키와 해당 값으로 데이터를 표현할 수 있듯, 관계형 모델은 관계(이하 릴레이션)로 데이터를 표현 하고 있다. key - value라는 개념은 python dictionary같은 형태로 많이 있어서 대강 이해가 되지만 도대체 릴레이션이란 무엇인지는 감이 잘 잡히지 않는다.

릴레이션은 속성 N개가 모인 집합인 제목과 속성 값의 집합인 행의 집합인 본체로 구성되어있다. SQL에 있어서 테이블에 해당 하는 것이라 생각하면 된다. 예를 들어서

이름, 나이, 학번 이라는 속성 3개가 모인 집합이 제목이고

이름취미학번

아채 처럼 해당 속성 값을 모두 가지는 행의 집합을 본체라고 볼 수 있다.

홍길동농구2022111811
길동홍축구2022114433
동홍길농구2022114553

행의 속성값은 제목에서 지정한 것과 일치해야하고, 제목에서 정의하지 않은 속성이 행에 존재하거나, 제목에 포함된 속성이 행에 존재하지 않으면 안 된다. 즉 이름 속성에는 성별, 키, 몸무게등이 아닌 ‘이름’이 들어와야 하고, 행에는 이름, 나이, 학번에 대한 값이 모두 있어야 하며, 키, 성별 같은 제목에서 정의 되지 않은 값이 있으면 안 된다. 1차 술어 논리와도 관계형 모델이 연결 되는데 자세히 공부해보고 싶으면 을 참고해 보자. 예를 들어 아래와 같은 테이블은 ‘X라는 인물은 조선 초기의 왕이었고, 그 사람은 Y살에 사망했다`라는 술어 논리에 참(TRUE)에 해당하는 값들의 집합이다.

nameage of death
태종74
정종63

테이블을 TRUE, FALSE로 생각해 볼 수 있다. 서울시 관악구에 살고있는 김철수는 ‘X라는 인물은 조선 초기의 왕이었고, 그 사람은 Y살에 사망했다`라는 술어 논리에 False이므로 아래 테이블에 해당하는 값이 아니다. 관계형 모델에 대해 대략적으로 알았으니 우리의 주제인 NULL로 돌아가 보자.

NULL이란 무엇인가?

값이 존재하지 않거나 값이 분명하지 않음을 나타내는 것이다. 공집합이 떠오를 수 있겠지만, 공집합은 요소가 0개인 실제하는 집합이며 존재하지 않는 집합 과는 명확히 다르다. 이 NULL의 존재가 연산에 어떤 영향을 줄 수 있는지 예시를 통해 살펴보자.

위 예시에 나온 테이블에서 SELECT * FROM users WHERE 취미 <> '농구' 라는 쿼리(나이가 15살인 행 집합의 속성 값을 모두 선택)를 실행 하는데 아래 처럼 취미에 NULL이 있는 행이 있다고 가정해보자.

길동이NULL2022123123

이 행은 쿼리의 조건에 해당하는가? NULL이니 취미가 농구가 아니라고 할 수 있을까? 아직 정해지지 않았을 뿐 농구가 될 가능성은 충분히 있다.NULL은 이처럼 모호함을 내포하고 있다.

NULL은 그냥 모든 쿼리에 False를 반환한다고 생각하면 되지 않을까?
-> 이렇게 된다면 행의 속성값은 제목에서 지정한 것과 일치해야하고, 제목에서 정의하지 않은 속성이 행에 존재하거나, 제목에 포함된 속성이 행에 존재하지 않으면 안 된다.라는 릴레이션의 정의에 반한다. 애초에 테이블에는 어떤 술어에 대한 참값이 와야하는데 False값이라니.. 앞 뒤가 맞지 않다. 예를 들어 SELECT * from T 에도 행의 속성 값 중 하나가 FALSE라면 해당 행은 select에 포함 될 수 없다.

그렇다면 속성 값에 TRUE, FALSE 뿐만 아니라 NULL이라는 제 3의 값을 포함 하면 어떨까? 간단한 진리표를 보며 연산이 얼마나 복잡해 지는지 살펴보자.

ABA AND BA OR B
TTTT
TFFT
FTFT
FFFF

NULL이 없다면 SQL 구문에서 행의 속성값은 TRUE / FALSE로 구분할 수 있다. SELECT * FROM table 과 같은 쿼리는 table의 모든 값이 TRUE이고 그 중 모든 속성을 선택한다는 의미이다. SELECT * FROM users WHERE 취미 <> '농구' 도 같다. 모든 행에서 취미 의 값이 농구인 행은 TRUE, 아니면 FALSE이다. JOIN ,GROUP BY, HAVING 등 모든 쿼리 명령어가 마찬가지이다. 위의 진리표로 표현을 할 수 있다.

하지만 여기에 NULL이 들어간다면 어떨까? 훨씬더 복잡해 진다! 쿼리는 TRUE, FALSE 뿐 아니라 제 3의 값을 신경 써야하기 때문에 동작 근본 자체가 바뀌어 버리고 옵티마이저의 구현에도 악영향을 미친다.

ABA AND BA OR B
TTTT
TFFT
TNULLNULLNULL
FTFT
FFFF
FNULLNULLNULL
NULLNULLNULLNULL

NULL safe한 연산자도 여기서 유추해 볼 수 있다. NULL이냐 NULL이 아니냐 즉 TRUE, FALSE의 연산이다.

NULL은 이처럼 골치아파 보인다.

NULL을 제거할 수 있는 방법은 무엇이 있을까?

적절하게 정규화 할 수 있는 방법이 있다. 아래 테이블을 정규화 해보면 NULL을 제거할 수 있다.

이름취미학번
홍길동농구2022111811
길동홍축구2022114433
동홍길농구2022114553
길동이NULL2022123123
이름학번
홍길동2022111811
길동홍2022114433
동홍길2022114553
길동이2022123123
이름취미
홍길동농구
길동홍축구
동홍길농구

Default 값으로 빈 문자열을 넣으면 어떻까? 이런 경우 취미가 농구가 아닌 사람만 뽑는 쿼리를 작성 할 때 빈 문자열에 대한 쿼리를 추가해 주어야 한다. NULL로 두고 NOT NULL 연산을 추가하는 것과 별다른 차이가 없다.


지금까지의 이야기는 관계형 모델을 고려한 이야기였다. SQL은 관계형 모델이 아니더라도 충분히 표현할 수 있다. 관계형 모델을 기반으로 하는 데이터가 아니라면

NULL을 적절히 사용해도 문제가 있다고 볼 수 없다.

profile
내가 배운 것 정리

0개의 댓글