#title NULL의 사용 [[TableOfContents]] ==== NULL에 대한 이해 ==== 일반적인 정보시스템에서의 정보인지 데이터인지는 '정보의 질'을 가지고 판단한다. '정보의 질'은 아래의 3가지로 평가를 한다. * 정확성 * 관련성 * 적시성 이것은 요청한 사람(또는 시스템)이 만족했다면 '정보'라고 불릴 수 있다. 통신쪽의 정보공학에서는 정보를 확률(Maximun 1)로 표현한다. '내일은 해가 동쪽에서 뜰 것이다'는 확률이 거의 1에 가까우므로 정보의 양이 적다고 이야기하며, '내일은 조직원 전체의 모든 직원에게 각각 1억[* '나는 내일 1억 인센티브 받는데..' 라고 하시는 분들은 저리 꺼져줘!]의 인센티브를 지급한다'는 확률이 거의 0에 가까우므로 정보의 양이 많다고 이야기 한다. 이러한 정보의 개념을 전제로 널값의 해석에서 '존재하지만 정보를 가지지 않음'을 생각해 보겠다. 예를 들어, 고객지원부의 이재학은 충성고객인 id가 'yasicom' 고객에게 자사의 상품소개에 대한 문서를 email로 전달하려고 한다. 그래서 개인정보 책임자는 CRM툴에서 아래와 id가 'yasicom'인 고객의 이름과 email을 요청했다. {{{ select name, email from memeber where id = 'yasicom' /* create table member ( id varchar(20) primary key , name varchar(20) not null , phone varchar(20) not null , email varchar(50) null ) */ }}} 결과는 {{{ name email ------- ------- 이재학 NULL }}} 이렇게 나왔다. 이것은 무엇을 말해주고 있는가? 널값의 해석에서와 같이 * 존재하지만 정보를 가지지 않음 * 값이 존재하는지 조차 모름 으로 해석할 수 있다. 즉, 고객 이재학은 email주소가 있음에도 불구하고 입력하지 않은 것을 수도 있거나 email주소가 없을 수도 있다는 뜻이 된다. 그럼 고객지원부의 홍길동은 아마도 아래의 2가지 중 하나를 선택할 것이다. * id가 'yasicom'인 고객의 전화번호를 알아내어 고객에게 전화 후, email주소를 알아내 업무 진행 (만약 email주소가 없다면 만들기를 권장한다. 등등) * email주소를 알 수 없으므로 또한 존재하는지도 모르므로 그냥 업무를 중단한다. 그렇다면 다시 '정보'라는 것으로 돌아가 보자. 일반적으로 정보는.. {{{데이터 -> [처리] -> 정보 -> 의사결정}}} 의 과정을 거친다. 분명히 데이터를 처리하였고 email주소가 null이라는 것을 알았다. 그래서 고객지원부의 홍길동은 email이 null이라는 것을 가지고 2가지 중 하나를 선택하는 '의사결정'을 하게 될 것이다. '정보'는 어떤 '의사결정'을 돕는 것이 최종 목적이다. 그렇므로 null도 정보가 될 수 있다. 분명히 좋은 쪽으로든 나쁜 쪽으로든 의사결정에 영향을 끼쳤으니까 말이다. '널값해석'은 '{{{null -> [처리] -> ?}}}'을 의미한다. 물음표(?)자리에는 뭐가 들어갈까? '정보'가 들어갈 수 밖에 없다. 그러므로 '널값해석1: 존재하지만 정보를 가지지 않은 경우'의 글에서는 '데이터' 보다는 '정보'가 더 어울리는 말이다. 즉, null값 자체는 데이터이고, null값 해석(처리)의 결과는 정보라고 할 수 있다. 1970년 IBM의 E.F. Code박사는 관계형 데이터 모델을 개발했다. 1985년에는 이상적인 관계형 데이터 모델을 정의하는 12규칙을 발표하였다. 그 중에는 NULL에 대한 정확한 언급이 있다. 원문: ||Rule 3: Systematic Treatment of Null ValuesA field should be allowed to remain empty. This involves the support of a null value, which is distinct from an empty string or a number with a value of zero. Of course, this can't apply to primary keys. In addition, most database implementations support the concept of a nun- null field constraint that prevents null values in a specific table column.|| 체계적인 Null 값 지원 : * DBMS는 널 값(알려지지 않거나 적용이 불가능한 데이터)을 사용하기 위해 체계적인 지원 * 널 값은 디폴트 값과 다르고 어떤 도메인(domain)에 대해서도 독립적이다. 이처럼 관계형 데이터 모델에서는 NULL에 대한 정의를 명확히 했다. 왜 E.F. Codd 박사는 12가지 규칙 (몇 가지 안되는 규칙 중에서)중에 NULL에 한 자리를 턱하니 내줬을까? 관계형 데이터 모델은 Legacy 시스템에는 없던 개념이었기 때문이다. insert tableA(num) values(1)은 num에는 1 값이 들어간 것이다. insert tableA(num) values(null)은 num에는 null 값이 들어간 것이다. 만약 num 컬럼에 not null이라고 선언이 되어 있다면 이것이 '무결성'을 위배한 것이 되겠다. not null컬럼에 대한 조회에서 null이 튀어나오는 것이 오류가 아닐까? 1도 독립적인 도메인이 있다. null도 독립적인 도메인이 있다. 왜 null도 독립적인 도메인을 가져야 할까? 생각해 보겠다. 다음 글은 참인가? * 1과 null은 같을 수도 있고, 다를 수도 있다. 필자가 보기에는 참이다. 같은 예로 이재학의 email은 'admin@databaser.net'이라고 가정하겠다. 단지 회원가입시 입력을 안했을 뿐이다. 이재학의 email이 null인가? 아니다. 단지 시스템의 관점에서 볼 때 알 수 없을 뿐이다. 그러므로 * 이재학의 email 주소는 null이다. * 이재학의 email 주소는 admin@databaser.net 이다 * 그러므로 null은 admin@databaser.net 이다. 라고 할 수 있다. (좀 어거지 입니다.) 이처럼 1 = null 이라고 가정하겠다. 그렇다면 1과 NULL은 같다고 봐야하나? 그럼 '이재학'은 null과 같다고 봐야하나? 그러므로 '이재학'과 1은 같다고 봐야 하나? 그렇다면 null이 홍길동도 지구도, 개도 소도 될 수 있으므로 '이재학 = 개'가 될 수 있다. 세상 모든 것이 null이 될 수 있다는 것이다. null이 세상 모든 것이라고 한다면 무엇이 될까? 아무것도 할 수 없다. 그러므로 null은 다른 어떤 도메인에 대해서도 독립적인 영역이 있어야 하는 것이 맞겠다. 가장 중요한 것은 현실을 얼마나 잘 반영할 수 있느냐가 아닐까 한다. 회원가입시 A4용지 4~5장 분량의 개인정보를 요청한다면 회원가입을 받는 궁극적인 목적을 달성할 수 있을까? 아마도 아닐 확률이 높을 것이다. 적절한 추상화레벨이 선택된다는 것(사람의 얼굴을 눈, 코, 입만 표현하면 되지 땀구멍 개수가 몇 개 인가까지는 알 필요 없다)이고, 우리는 이것을 컴퓨터세계에 반영할 것이다. 즉, null이 존재해야만 실현(컴퓨터 세계에 반영) 가능하다는 뜻이겠다. 반대로 null 없고 모든 데이터가 not null이라면 어떨까? 글을 쓰고 있는 놈은 생각만해도 우울하다. 현실을 잘 반영해서 컴퓨터 세계로 옮겨 놓는(이게 전산화죠) 궁극적인 목적을 달성했다면 NULL이 존재함이 '오류'가 아님을 알려주는 결과가 아닐까? === NULL 연산 === NULL값에 대한 연산은 무한대의 표시와 비슷하다. 기본적으로 1 + NULL도 NULL이고, NULL* 1도 NULL이고, NULL+ NULL도 NULL이다. 가끔은 이런 형태의 쿼리를 볼 수 있을 것이다. {{{ --1 SELECT SUM(ISNULL(ROYALTY, 0)) FROM TITLES --2 SELECT SUM(ROYALTY) FROM TITLES }}} 두 쿼리의 결과는 NULL값이 포함되어 있건, 포함되어 있지 않건 당연히 같다. 일반적으로 생각해 보아도 당연히 같다. ANSI에서는 일반적인 연산과 집계연산에 대해서 다른 NULL값의 처리를 권하고 있고, DBMS제품도 당연히 이를 따르고 있다. 여러 개의 값이 있는데 열심히 합계를 구하고 있는데, 그 중에 NULL값이 있다면, 아무리 더한다고 해도 NULL값이 될 수 밖에 없기 때문에 집계함수에서는 NULL값을 포함시키지 않고 있다. SUM()의 경우는 같으나 다음과 같이 평균 연산을 하면 그 결과는 확연히 달라진다. 이런 경우라면 NULL값의 의미를 정확히 정의하고 있는 경우라야 맞다. 그렇지 않다면 쓸 때 없이 함수를 사용하여 모든 행에 ISNULL() 함수를 적용해줄 필요성이 없게 되는 것이다. {{{ --1 SELECT AVG(ISNULL(ROYALTY, 0)) FROM TITLES --2 SELECT AVG(ROYALTY) FROM TITLES }}} 왜냐하면 ISNULL 함수를 사용해서 NULL 값을 0으로 치환했기 때문에 평균수치는 상당히 낮아질 것이란 이야기다. 또 한가지 웃긴 것은 COUNT(*) 이다. COUNT()함수는 일반적으로 NULL값을 포함한다. NULL을 가진 행도 분명히 행이기 때문에 행의 갯수를 세는 이 함수는 NULL값을 포함한다. 그러나 우스운 것은 COUNT(*), 와 COUNT(COLUMN_NAME)과는 틀린 결과가 나타나기 때문이다. {{{ --1 SELECT COUNT(*) FROM TITLES --결과는 18 --2 SELECT COUNT(ROYALTY) FROM TITLES--결과는 16 }}} 즉, COUNT(COLUMN_NAME)의 경우는 NULL값을 제외하고 연산을 한다. 주의하기 바란다. 또한 제품마다 IS NULL의 사용에 인덱스를 적용시키는지 아닌지도 틀리다. 오라클의 경우 IS NULL이라고 WHERE 절에 명시하면 풀스캔을 하며, MSSQL SERVER의 경우는 인덱스를 사용할 수 있게 한다. NULL값은 물리적인 데이터베이스의 표현이다. 그러나 현실을 모델링하여 얻은 결과가 물리적으로 반영된다는 사실을 잊지 말아야 한다. 물리적으로 표현된 것은 현실과의 일치성을 가지는 것이 당연하다. 현실을 무시한 채 시스템이 개발된다면 모델링의 결과를 무시했거나 모델링이 잘못된 것이거나, 모델러가 업무분석을 제대로 못했다는 결론이다. 데이터베이스쟁이는 그야말로 충실히 현실을 반영해야 하는 것이다. 물리적으로는 NULL 허용대신 Defualt를 사용할 것을 권장하는 것이 애매함을 없앨 수 있다. ==== DBMS마다 다른 연산 ==== MS-SQL Server의 경우 NULL은 가장 작은 값으로 인식한다. 하지만 Oracle Server에서는 NULL을 가장 큰 값으로 인식한다. 정렬하여 데이터의 순서로 무엇인가 연산을 한다면 DBMS에서 NULL이 어떻게 처리되는지 확인해 볼 필요가 있겠다.