[SQL] Number와 boolean은 NOT NULL 로 선언하자.
비즈니스적으로 기본값이 있는 경우는 NOT NULL로 선언을 하지만 그런 경우가 아니라면 유연하게 대처하기 위해 Nullable로 선언을 한다.
테이블의 Boolean과 Number 타입의 컬럼을 Nullable로 선언하면 다양한 문제가 발생할 수 있어 추천하지 않는다.
1. 의미 혼란
컬럼을 Nullable로 설정할 경우, 기본값이 NULL 이 되므로, 의미가 혼란스러워 질 수 있다.
예를들어, boolean 컬럼의 값은 true, false, null 세 가지 상태가 될 수 있다. ('참', '거짓', '미확인')
일부 상황에서는 유용할 수 있지만, 대부분의 경우 논리적 복잡성이 추가되는 일이라 코드에서 이를 처리해야 할 경우 복잡성을 추가한다.
- boolean 타입의 컬럼은 null 값과 false가 어떤 의미 차이가 있는지
- Number 타입의 컬럼은 null 값과 0이 어떤 의미 차이가 있는지
이를 구분해서 사용해야 한다.
2. SQL의 복잡도
null을 가진 컬럼은 쿼리를 복잡하게 만든다.
2-1. Null 제외 후 계산
예를 들어, 다음과 같은 테이블의 row 데이터가 있다고 하자.
price - 1000
price - null
price - 0
price - 1000
이 상태의 평균값은 얼마일까?
4개 row의 총 합은 2,000이고 총 4개이니 500이 예상되지만
실제로 쿼리를 수행하면 666.6666...이 나온다.
SELECT AVG(price) FROM table;
PostgreSQL의 AVG() 함수는 평균을 계산할 때 Null 값은 자동으로 무시된다. (NULL 값은 계산에 포함하지 않는다.)
NULL값을 포함하여 계산을 할 경우, NULL 값을 특정 값으로 바꾸는 함수인 COALESCE()를 사용해야 한다.
SELECT AVG(COALESCE(price, 0)) FROM table;
물론, NULL 값을 제외하고 실제 채워진 값들에 한해서만 결과를 가져와야 하는 경우도 있다.
많은 집계함수에서 NULL 데이터의 포함/미포함에 대해 고민해야 하고, 그에 따른 추가적인 SQL 함수를 고려해야 한다.
매번 COALESCE를 통한 추가 SQL를 사용하거나, 잘못된 결과를 사용하거나 등의 위험을 항상 안고가야 할 정도로 NULL 값을 유지해야 할 필요가 있는지 고려해야 한다.
2-2. IS NULL
단순한 SQL 조회문을 만들 때도 이에 대한 고려가 항상 포함된다.
보통 false와 NULL 혹은 0과 NULL은 함께 조건에 사용될 때가 많다.
하지만, NULL 값을 조회하기 위해서 일반적인 비교 연산자 (=, <>, <, >, IN() 등)을 사용할 수 없으며 IS NULL 혹은 IS NOT NULL을 사용해야 한다.
그래서 false와 NULL을 함께 조회하려면 OR 연산자 쿼리를 작성해야만 한다.
SELECT * FROM users WHERE is_active IS FALSE OR is_active IS NULL;
하나의 상태값을 조회하기 위해서 쿼리가 복잡해질 수 있다.
3. 애플리케이션 코드 복잡성
컬럼에 NULL을 허용하면, 이 컬럼을 사용하는 애플리케이션 코드에서 항상 NULL 체크를 수행해야 한다.
이는 코드 복잡성을 증가시킨다.
결국 Nullable 컬럼의 데이터를 어플리케이션에서 조회하면 숫자 연산에 대해 0과 NULL 상태 모두 항상 조건을 걸거나 NULL -> 0 (또는 특정한 숫자값)으로 변경해야 한다.
val price = getPrice(); // nullable
val result = price ? price : 0;
결론
Number와 Boolean 타입에서 0과 NULL의 차이가 명확하거나 false와 NULL의 차이가 명확한 경우에만 Nullable로 선언하며 가능하면 항상 NOT NULL로 기본값을 보장하는 것이 좋다.
만약, null과 false, 0의 구분이 필요한 상황이면 그게 정말 null로 구분해야 하는 것인지 고민하고, 상태를 나타내는 Enum을 고려하는 것이 좋을 수도 있다.
예를들어, 합격 여부에 대한 항목이 필요하다고 하면
// AS-IS = boolean과 NULL을 함께 사용
pass.isPassed // 합격 여부
- null : 합격 발표 전
- false : 합격 발표 - 불합격
- true: 합격 발표 - 합격
// TO-BE = Enum을 사용
pass.status
- READY : 합격 발표 전
- FAIL : 합격 발표 - 불합격
- PASS : 합격 발표 - 합격
이에 대해서는 객체 생성단계에서 무조건 기본값을 할당하는 것이 좋다.
물론, NULL과 0의 차이가 명확하게 구분된 상황이라면 이에 대해 정확하게 주석을 남겨야 하며 이 주석의 범위는 테이블 컬럼 주석과 ORM 영역 모두에 해당한다.
출처
https://jojoldu.tistory.com/718
'programming language > SQL' 카테고리의 다른 글
[CUBRID] 테이블 컬럼 추가 / 변경 쿼리 (2) | 2022.12.14 |
---|---|
[SQL] 효율적인 DB INDEX(인덱스) 설정 (0) | 2022.03.24 |
댓글 개