😗
SQL Guide
  • 😀SQL 전문가 가이드의 가이드
  • 😦과목1 데이터모델의 이해
    • ⭐제1장 데이터 모델링의 이해
      • 🌠1-1-1. 데이터 모델의 이해
      • 🌠1-1-2. 엔터티(Entity)
      • 🌠1-1-3. 속성(Attribute)
      • 🌠1-1-4. 관계(Relationship)
      • 🌠1-1-5. 식별자
    • ⭐제2장 데이터 모델과 SQL
      • 🌠1-2-1. 정규화
      • 🌠1-2-2. 관계와 조인의 이해
      • 🌠1-2-3. 모델이 표현하는 트랜잭션의 이해
      • 🌠1-2-4. Null 속성의 이해
      • 🌠1-2-5. 본질식별자 vs. 인조식별자
  • 😧과목2. SQL 기본과 활용
    • ⭐제1장 SQL 기본
      • 🌠2-1-1. 관계형 데이터베이스 개요
      • 🌠2-1-2. SELECT문
      • 🌠2-1-3. 함수(FUNCTION)
      • 🌠2-1-4. WHERE 절
      • 🌠2-1-5. GROUP BY, HAVING 절
      • 🌠2-1-6. ORDER BY 절
      • 🌠2-1-7. 조인
      • 🌠2-1-8. 표준 조인
    • ⭐제2장 SQL 활용
      • 🌠2-2-1. 서브 쿼리
      • 🌠2-2-2. 집합 연산자
      • 🌠2-2-3. 그룹 함수
      • 🌠2-2-4. 윈도우 함수
      • 🌠2-2-5. Top N 쿼리
      • 🌠2-2-6. 계층형 질의와 셀프 조인
      • 🌠2-2-7. PIVOT 절과 UNPIVOT 절
      • 🌠2-2-8. 정규 표현식
    • ⭐제3장 관리 구문
      • 🌠2-3-1. DML
      • 🌠2-3-2. TCL
      • 🌠2-3-3. DDL
      • 🌠2-3-4. DCL
  • 😨과목3. SQL 고급 활용 및 튜닝
    • ⭐제1장 SQL 수행 구조
      • 🌠3-1-1. 데이터베이스 아키텍처
      • 🌠3-1-2. SQL 처리 과정
      • 🌠3-1-3. 데이터베이스 I/O 메커니즘
    • ⭐제2장 SQL 분석 도구
      • 🌠3-2-1. 예상 실행계획
      • 🌠3-2-2. SQL 트레이스
      • 🌠3-2-3. 응답 시간 분석
    • ⭐제3장 인덱스 튜닝
      • 🌠3-3-1. 인덱스 기본 원리
      • 🌠3-3-2. 테이블 액세스 최소화
      • 🌠3-3-3. 인덱스 스캔 효율화
      • 🌠3-3-4. 인덱스 설계
    • ⭐제4장 조인 튜닝
      • 🌠3-4-1. NL 조인
      • 🌠3-4-2. 소트 머지 조인
      • 🌠3-4-3. 해시 조인
      • 🌠3-4-4. 스칼라 서브쿼리
      • 🌠3-4-5. 고급 조인 기법
    • ⭐제5장 SQL 옵티마이저
      • 🌠3-5-1. SQL 옵티마이징 원리
      • 🌠3-5-2. SQL 공유 및 재사용
      • 🌠3-5-3. 쿼리 변환
    • ⭐제6장 고급 SQL 튜닝
      • 🌠3-6-1. 소트 튜닝
      • 🌠3-6-2. DML 튜닝
      • 🌠3-6-3. 데이터베이스 Call 최소화
      • 🌠3-6-4. 파티셔닝
      • 🌠3-6-5. 대용량 배치 프로그램 튜닝
      • 🌠3-6-6. 고급 SQL 활용
    • ⭐제7장 Lock과 트랜잭션 동시성 제어
      • 🌠3-7-1. Lock
      • 🌠3-7-2. 트랜잭션
      • 🌠3-7-3. 동시성 제어
Powered by GitBook
On this page
  • 과목1. 데이터 모델링의 이해
  • 제2장 데이터 모델과 SQL
  • 제4절 Null 속성의 이해
  • 1. Null 값의 연산은 언제나 Null이다
  • 2. 집계함수는 Null 값을 제외하고 처리한다

Was this helpful?

  1. 과목1 데이터모델의 이해
  2. 제2장 데이터 모델과 SQL

1-2-4. Null 속성의 이해

과목1. 데이터 모델링의 이해

제2장 데이터 모델과 SQL

제4절 Null 속성의 이해

DBMS를 사용하다 보면 Null 값으로 인한 많은 특이사항들을 접하게 된다. Null 값이 가지는 특성을 이해하지못한다면 데이터 오류를 경험할 수 있으므로 반드시 숙지해야 할 부분 중 하나다. 다음 모델을 보고 사례별로 Null값에 대해 알아보자.

[그림 I-2-20] 주문 모델

[그림 I-2-20] 주문 모델을 보면 주문금액과 주문취소금액이 Null 허용인 것을 알 수 있다. IE 표기법에서는Null 허용여부를 알 수 없지만, 바커 표기법에서 속성 앞에 동그라미가 Null 허용 속성임을 의미한다. 즉 해당속성은 Null 값을 가질 수 있는 속성으로 몇 가지 특성이 존재한다.

1. Null 값의 연산은 언제나 Null이다

Null 값은 공백이나 숫자 과는 전혀 다른 의미다. 아직 정의되지 않은 미지의 값' 또는 현재 데이터를 입력하지못하는 경우'를 의미한다. 즉 Null은 값이 존재하지 않음을 말한다.

[표 I-2-7] 주문 데이터

주문번호
주문금액
주문취소금액

1100001

100,000

20,000

1100002

15,000

[표 I-2-]은 주문 모델에 들어 있는 데이터다. 본 데이터를 바탕으로 다음 SQL의 결과를 예측해보자.

SELECT 주문금액 - 주문취소금액 COL1
     , NVL(주문금액 주문취소금액,0) COL2
	 , NVL(주문금액,0)-NVL(주문취소금액,0) COL3
  FROM 주문

COL1, COL2, COL3는 최종 주문금액을 구하는 산식이다. 최종 주문금액은 각 주문의 주문금액에서 취소된 주문금액을 제외한 결과다. 이와 같은 요건에서는 주로 COL1, COL2, COL3의 방식으로 SQL을 작성할 것이다. 동일한 목적으로 작성된 COL1, COL2, COL3의 결과는 모두 동일할까?

[표 I-2-8] SQL 수행 결과

COL1
COL2
COL3

80,000

80,000

80,000

0

15,000

[표 I-2-8] 결과를 보면 Null 값이 포함되었을 경우 COL1, COL2, COL3의 결과가 모두 다르게 출력하는 것을 알 수 있다. 이유는, Null 값의 연산은 언제나 Null이기 때문이다. Null은 아직 값이 존재하지 않는 것으로, 아무것도 존재하지 않는 값에 연산이 가능할까? 불가능하다. 그렇기에 Null 연산은 언제나 Null을 결과로 반환한다. 그럼 Null 값으로 가능한 연산은 무엇일까? 바로 'IS NULL, IS NOT NULL'밖에 없다. Null 값이냐? Null 값이 아니냐?이 두 가지 연산만 가능하다. 논리적으로 천천히 생각해보면 충분히 수긍할 수 있을 것이다. 또한 위 SQL 이해를 위해서는 NVL 함수를 알아야 한다. NVL 함수는 첫 번째 인자값의 결과가 Null일 경우 두 번째 인자 값을 반환하는 함수다. 이러한 배경지식을 토대로 하나씩 풀어보자.

주문금액 - 주문취소금액 COL1

COL1은 최종주문금액을 구하기 위해 Null 값을 전혀 고려하지 않고, 주문금액에서 주문취소금액을 제외한 방식이다. Null 값이 존재하지 않는 첫 번째 행의 결과는 정상적으로 ‘100,000 - 20,000 = 80,000'이 나오겠지만, Null값이 존재하는 두 번째 행의 결과는 15,000 - Null = Null'이라는 Null 값을 반환하게 된다.

NVL (주문금액 주문취소금액,0) COL2

COL2는 주문금액에서 주문취소금액을 제외한 결과에 대해 NVL 처리를 하였다. COL1보다는 Null 값에 대한 고려한 것처럼 보이지만 과연 올바른 결과를 출력할까? Null 값이 존재하지 않는 첫 번째 행의 결과는 정상적으로 '100,000 - 20,000 = 80,000'이 나온다. 하지만 두 번째 결과는 15,000 - Null = Null' 로 Null 값을 반환한다. 이때 NVL 함수로 인하여 Null 값을 0으로 변환하여 반환한다. 두 번째 행의 최종주문금액이 0이 맞는가? 우리가 원하는 값은 15,000일 것이다.

NVL(주문금액,0)-NVL(주문취소금액,0) COL3

COL3은 각 속성별로 NVL 처리를 하고, 이후 주문금액에서 주문취소금액을 제외하였다. Null 값에 대한 이해를 숙지한 경우로 볼 수 있다. 첫 번째 행의 결과는 정상적으로 '100,000 - 20,000 = 80,000'이 나온다. 문제는 Null값을 보유한 두 번째 행의 경우다. 속성별로 Null일 경우 NVL 함수를 사용하여 0으로 변환 후 연산을 처리하였다. 그리하여 '15,000 - 0 = 15,000'이라는 결과가 나오게 된다. 우리가 원하던 결과를 반환할 수 있게 되었다. 이처럼 Null에 대한 특성을 인지하지 않는다면 COL1, COL2처럼 잘못된 결과를 반환할 수 있다는 것을 숙지해야 한다. =

2. 집계함수는 Null 값을 제외하고 처리한다

[표 I-2-9] 주문 데이터

주문번호
주문금액
주문취소금액

1100001

100,000

1100002

15,000

1100003

40,000

1100004

45,000

1100005

100,000

[표 I-2-9은 주문 모델에 다른 데이터 입력해 보았다. 주문취소금액은 전부 Null 값으로 아직 취소된 주문이 없는 상태다. 본 데이터를 바탕으로 다음 SQL 결과를 예측해보자.

SELECT SUM(주문금액) - SUM(주문취소금액) COL1
     , NVL(SUM(주문금액 주문취소금액),0) COL2
	 , NVL(SUM(주문금액), 0)-NVL(SUM(주문취소금액),0) COL3
  FROM 주문

COL1, COL2, COL3는 최종주문금액 총합을 구하는 산식이다. 최종주문금액은 각 주문의 주문금액에서 취소된 주문금액을 제외하고, 총합은 이를 합산한 결과다. 이와 같은 요건이 있을 경우 주로 COL1, COL2, COL3 방식으로 SQL을 작성할 것이다. 동일한 목적으로 작성된 COL1, COL2, COL3의 결과는 모두 동일할까?

[표 I-2-10] SQL 수행 결과

COL1
COL2
COL3

0

300,000

[표 I-2-10]의 결과를 보면 COL1, COL2, COL3은 모두 다른 결과를 출력한다. 앞서 배웠듯 Null 값의 연산은 언제나 Null이다. 또한 집계함수는 Null 값의 경우는 제외하고 연산한다. SUM 함수는 정의된 칼럼의 값을 모두 합산하는 함수로서 Null 값이 들어올 경우 이는 제외하고 처리한다.

SUM(주문금액) - SUM(주문취소금액) COL1

COL1은 속성별로 SUM 함수로 합산을 먼저 수행하고, 총주문금액에서 총주문취소금액을 제외하였다. SUM(주문금액)은 주문금액을 모두 합산하라는 의미다. [표 I-2-9]에서 주문금액을 모두 합산한 결과는 300,000이다. SUM(주문취소금액)은 주문취소금액을 모두 합산하라는 의미다. 주문취소금액은 한 건도 존재하지 않기에 합산한 결과는 Null이 된다. 즉 '300,000 - Null'을 수행하게 되고 결과로 Null 값을 반환한 것이다.

NVL(SUM(주문금액 주문취소금액),0) COL2

COL2는 주문금액에서 주문취소금액을 제외한 결과를 SUM 함수로 합산하고, 최종결과가 Null일 경우 0을 반환하는 NVL 처리를 하였다. SUM(주문금액- 주문취소금액)은 주문별로 주문금액-주문취소금액'을 한 결과를 모두 합산하라는 의미다. 첫 번째 행은 '100,000 - Null'을 하여 결과로 Null 값을 반환한다. 두 번째 행도 '15,000 - Null'을 수행하여 결과로 Null 값을 반환한다. 세 번째 · 네 번째 · 다섯 번째 모두 같은 결과로 Null 값을 반환하며, 이를 합산한 결과는 Null이 된다. 그리고 최종적으로 NVL 함수를 수행한다. 이는 값이 Null일 경우 두 번째 인자값인 0으로 결과를 반환하는 것으로 최종결과로 0을 반환한다.

NVL (SUM(주문금액), 0)-NVL(SUM(주문취소금액),0) COL3

COL3은 각 속성별로 SUM 함수로 합산하고 최종결과가 Null일 경우 0을 반환한 후, 총주문금액에서 총주문취소금액을 제외하였다. NVL(SUM(주문금액),0)은 주문금액을 모두 합산하고, 합산한 결과가 Null일 경우 0을 반환하라는 의미다. 마찬가지로 NVL(SUM(주문취소금액), ) 역시 주문취소금액을 모두 합산하고 결과가 Null일 경우 0을 반환하게 된다. 이를 수행하면 최종적으로 '300,000 - 0이 되며, 결과로 300,000를 반환하게 된다. 총주문금액은 300,000으로 원하는 결과를 얻을 수 있다.

집계함수의 경우 Null 값을 제외한다는 특성을 이해하여야만 올바른 결과를 출력할 수 있다. 과연 제대로 숙지하였는지 문제 하나를 더 풀어보자.

[표 I-2-11] 주문 데이터

주문번호
주문금액
주문취소금액

1100001

100,000

20,000

1100002

15,000

1100003

40,000

10,000

1100004

45,000

10,000

1100005

100,000

10,000

[표 I-2-11]은 주문 모델에 또 다른 데이터를 입력해 보았다. 주문취소금액의 평균은 얼마일까? 알고 있는 평균을 구하는 공식은 아래와 같다.

평균 = 자료전체의 합 / 자료의 개수

본 데이터에서 구하고자 하는 평균값은 10,000일까? 12,500일까? 즉 자료의 개수에 Null 값을 포함해야 하는지 제외해야 하는지를 정의해야 평균을 구할 수 있다.

SELECT SUM(주문취소금액)/COUNT(*) COL1
     , AVG(주문취소금액) COL2
  FROM 주문

위와 같은 SQL에서 COL1과 COL2의 결과는 다르게 나온다.

[표 I-2-12] SQL 수행 결과

COL1
COL2

10,000

12,500

[표 I-2-12]의 결과를 이해할 수 있어야 한다. 왜 COL1과 COL2가 다른 결과를 출력하는지 살펴보자.

NVL(SUM(주문취소금액),0)/COUNT(*) COL1

COL1은 주문취소금액의 합계를 총건수로 나누었다. 익히 알고 있는 평균을 구하는 공식으로 '50,000 / 5'의 연산 결과로 10,000을 반환하였다.

AVG(주문취소금액) COL2

COL2는 평균값을 구하는 집계함수 AVG를 사용하였다. 집계함수는 Null 값을 제외한다는 특성으로 '50,000/4'로 연산하였다. 주문취소금액이 Null인 주문번호 1100002 데이터를 제외한 것이다. 그 결과로 12,500을 반환하였다. 원하는 평균값은 주문취소금액이 발생한 주문만을 대상으로 해야 하는지, 아니면 전체를 대상으로 해야 하는지에 대한 정의가 먼저 정해져야 올바른 값을 구할 수 있다. 하지만 그전에 집계함수가 가지고 있는 특성을 이해해야 함을 위 사례를 통해 알 수 있다.

사실 이밖에도 Null 값에 문제가 발생하는 경우는 많다. 중요한 것은 속성에 Null 값이 존재한다면 이처럼 많은 사항을 고려해야 한다는 점이다. 그렇기에 모델을 생성할 때, 업무를 정확히 파악하여 Null 허용 여부를 판단해야 한다.

모델을 생성하는 모델러 혹은 개발자는 속성의 의미를 정확히 파악하고 Null 값을 허용하였는지, 아니면 너무 쉽게 Null 값을 허용해 Null에 대한 관리비용을 증가시킨 것은 아닌지 다시 한번 생각해 보아야 할 것이다.

Previous1-2-3. 모델이 표현하는 트랜잭션의 이해Next1-2-5. 본질식별자 vs. 인조식별자

Last updated 3 years ago

Was this helpful?

출처 : 데이터온에어 – 한국데이터산업진흥원()

😦
⭐
🌠
https://dataonair.or.kr