14장. 모르는 것에 대한 두려움
14.1 목표: 누락된 값을 구분하기
- 테이블과 쿼리에서 NULL 값을 생산적으로 사용할 수 있는 다양한 방법이 있다.
- 여전히 일하고 있는 직원의 퇴사일과 같이, 행을 생성할 때 값을 알 수 없는 곳에 NULL을 사용할 수 있다.
- 전기만 사용하는 자동차에 대한 연료 효율과 같이, 주어진 칼럼이 주어진 행에서 적용 가능한 값이 없는 경우에 NULL값을 사용할 수 있다.
- 함수에 인수로 DAY('2009-12-32')와 같이 유효하지 않은 값이 입력되는 경우 NULL을 리턴할 수 있다.
- 외부 조인에서 매치되지 않는 행의 칼럼 값의 자리를 채우는 데 NULL 값을 사용한다.
14.2 안티패턴: NULL을 일반 값처럼 사용
- SQL에서는 NULL을 0이나 false 또는 빈 문자열과 다른 특별한 값으로 취급한다.
(Oracle이나 Sybase에서는 NULL이 길이가 0인 문자열과 동일 - NULL을 문자열 데이터로 취급할때)
수식에서 NULL 사용
- NULL은 0과 같지 않다. NULL + 10 = NULL
- 표준 SQL에서는 어떤 문자열도 NULL과 연결하면 NULL (Oracle, Sybase 예외)
- NULL은 false와도 같지 않다.
- NULL이 들어간 불리언 수식은 AND, OR, NOT을 사용하더라도 항상 NULL
NULL을 가질 수 있는 컬럼 검색
SELECT * FROM Bugs WHERE assigned_to = 123;
SELECT * FROM Bugs WHERE NOT (assigned_to = 123);
- 두 쿼리 모두 assigned_to 컬럼의 값이 NULL인 행은 리턴하지 않는다.
SELECT * FROM Bugs WHERE assigned_to = NULL;
SELECT * FROM Bugs WHERE assigned_to <> NULL;
- NULL과의 비교는 그냥 NULL.
- 두 쿼리 모두 assigned_to 컬럼의 값이 NULL인 행을 리턴하지 않는다.
쿼리 파라미터로 NULL 사용
SELECT * FROM Bugs WHERE assigned_to = ?;
파라미터를 받는 SQL에서는 NULL을 다른 일반적인 값처럼 사용하기 어렵다. NULL을 파라미터로 사용할 수 없다.
문제 회피하기
- NOT NULL로 선언 후 의미 없는 기본값 부여.
- 숫자 컬럼의 경우 SUM()이나 AVG() 같은 계산시 값이 포함 된다.
14.3 안티패턴 인식 방법
- "assigned_to 칼럼에 아무 값도 설정되지 않은 행을 어떻게 찾을 수 있지?"
- "애플리케이션에서 몇몇 사용자의 전체 이름이 표시되지 않아. 데이터베이스에서는 분명 볼 수 있는데."
- "이 프로젝트의 전체 작업시간 보고서에 우리가 완료한 몇몇 버그만 포함되어 있어! 그러니까 우선순위를 할당한 것들만 포함이 되어 있네"
- "Bugs 테이블에서 '알 수 없음'을 나타내는 데 예전에 사용하던 문자열을 사용할 수 없다는 것을 확인했기 때문에,
다른 어떤 값을 사용해야 할 지 그리고 데이터를 변환해서 우리 코드가 새로운 값을 사용하도록 하는데 개발 기간이 얼마나 필요할지 논의하기 이한 회의가 필요해."
14.4 안티패턴 사용이 합당한 경우
- NULL을 사용하는 것은 안티패턴이 아니다. NULL을 일반적인 값처럼 사용하거나 일반적인 값을 NULL처럼 사용하는 것이 안티패턴이다.
14.5 해법: 유일한 값으로 NULL을 사용하라
스칼라 수식에서의 NULL
불리언 수식에서의 NULL
NULL 검색하기
SELECT * FROM Bugs WHERE assigned_to IS NULL;
SELECT * FROM Bugs WHERE assigned_to IS NOT NULL;
- SQL-99 표준에서는 IS DISTINCT FROM 이란 비교연산자가 정의됨. <> 과 비슷하게 동작. 피연산자가 NULL이더라도 항상 ture 또는 false를 리턴.
SELECT * FROM Bugs WHERE assigned_to IS NULL OR assigned_to <> 1;
SELECT * FROM Bugs WHERE assigned_to IS DISTINCT FROM 1;
code:sql}
SELECT * FROM Bugs WHERE assigned_to IS DISTINCT FROM ?;
* 쿼리 파라미터로 리터럴 값이나 NULL을 보내고 싶을 때 이 연사자 사용.
* PostgreSQL, IBM DB2, Firebird 지원, Oracle과 Microsoft SQL Server는 미 지원. MySQL은 전용 연산자 <=> 제공.
h3. 컬럼을 NOT NULL로 선언하기
* NULL 값이 애플리케이션 정책을 위반하거나 또는 의미가 없는 경우에는 컬럼에 NOT NULL 제약조건을 선언하라.
* 필요에 따라서는 DEFAULT 값을 정의해라.
h3. 동적 디폴트
* 주어진 컬럼이나 수식에, 특히 특정 쿼리에서만 디폴트 값을 설정하는 방법이다. coalesce() 함수 사용.
(NVL() 혹은 ISNULL() 함수 사용)
{note}
어떤 데이터 타입에 대해서든 누락된 값을 뜻하는데는 NULL을 사용하라.
{note}
h2. 문서에 대하여
* 최초작성일 : 2011년 12월 10일
* 이 문서는 [오라클클럽|http://www.gurubee.net] [코어 오라클 데이터베이스 스터디|7차 코어 오라클 데이터베이스 스터디] 모임에서 작성하였습니다.
* {color:blue}{*}이 문서의 내용은 인사이트(insight) 에서 출간한 'SQL AntiPatterns : 개발자가 알아야 할 25가지 SQL 함정과 해법'를 참고하였습니다.*{color}