[DB] 'is not NULL'과 '!= NULL'은 다르다?
팀원분이 비슷해 보이는 두 쿼리문를 보내시며 둘의 결과가 서로 다르다고 보내주셨다. 두 쿼리의 차이점은 오직 NULL 체크를 is not NULL로 하냐, != NULL로 하냐의 차이였다.
쉬운 이해를 위해 사용자를 조회하는 쿼리가 있다고 가정해보겠다.
-- 1: is not NULL 사용 (10 Results)
select *
from member
where age is not NULL
limit 1;
-- 2: != NULL 사용 (0 Results)
select *
from member
where age != NULL
limit 1;
is not NULL에서는 조회 결과가 있지만, != NULL에서는 조회 결과가 없었다. 왜 그럴까?
SQL에서는 NULL 값을 =, <> 같은 비교 연산자를 이용해 비교할 수 없다고 한다. 이걸 비교할 수 있게 만들고 싶다면 SET ANSI_NULLS를 true로 설정하면 된다. (참고: microsoft - SET ANSI_NULLS) 개인적인 의견을 한마디만 덧붙여보자면 SQL 표준을 준수하기 위해 생긴 설정인데 일부러 표준을 위반하도록 설정을 바꾸는 것이 꺼려졌다.
여기서 SQL의 NULL을 어떻게 취급하는지 좀 더 알아보고자 한다. SQL에서 NULL이란 무엇일까? 다른 언어들과 비슷하다. 데이터 값이 존재하지 않음을 나타내기 위한 특수한 키워드다. 관계형 데이터베이스 모델의 창시자인 E. F. Codd말을 인용해보자면, SQL NULL은 '모든 RDBMS(관계형 데이터베이스 시스템)이 누락된 정보나 적용할 수 없는 정보의 표현을 지원해야 한다', 는 요구 사항을 충족하는 역할을 한다. null은 특정한 값이 아니라 기호나 표시, 특별한 키워드 정도로 인식하면 될 것 같다. (영어로는 marker라고 자주 표현되는 듯 하다.)
NULL은 값이 아니기 때문에 많은 연산들에서 의외의 결과를 이끌어낸다. 몇 가지 예제를 들고와보았다. 아래의 연산 결과를 한번 예측해보자. (참고로 concat은 문자열을 연결해주는 함수이다.)
select 10 * NULL;
select NULL / 1;
select concat('Hello ', null, 'World!');
정답은 모두 NULL이다. NULL은 특정 값이 아니라 값의 부재에 대한 표시이기 때문에 NULL에 대해 연산자를 사용해도 NULL이 반환된다. 여기서 헷갈리면 안되는 부분은, 어떤 연산자를 사용하든 항상 NULL이 된다는 뜻이 아니라는 점이다. 아래와 같은 경우에는 결과가 true를 의미하는 1이 반환된다. (oracle에서는 ||가 or가 아닌 concat으로 사용되므로 결과가 다를 수 있다. oracle이라면 || 대신 or을 사용해 예제를 실행해보자.)
select NULL || true;
참고
- https://stackoverflow.com/questions/5658457/not-equal-operator-on-null
- https://en.wikipedia.org/wiki/Null_%28SQL%29
- https://www.w3schools.com/SQL/sql_null_values.asp