전문가칼럼

DBMS, DB 구축 절차, 빅데이터 기술 칼럼, 사례연구 및 세미나 자료를 소개합니다.

관계형 데이터베이스 분석/설계 기본으로 돌아가자! 관계형 이론을 근간으로(4)

전문가칼럼
DBMS별 분류
Etc
작성자
admin
작성일
2021-02-22 16:05
조회
2213
영역 무결성 (Domain Integrity)

엔코아 정보 컨설팅
장희식

이 특성은 데이터 무결성의 영역 무결성을 규정하는 것으로 이와 함께 설명하고자 한다. 영역 무결성이란 테이블 내 컬럼이 가져야 하는 업무 규칙들 즉, Data Type, Length, Not Null 여부, 허용값(Permitted Value)값 등에 대한 비즈니스 규칙(Business Rule = 업무규칙)을 규정하는 것이다. Oracle 데이터베이스의 경우 Physical하게는 PK Constraint와 FK Constraint를 제외한 각종 Constraint라는 것들이다.
옵티마이져(Optimizer)의 간단한 원리를 설명하며 논리를 전개해 나가겠다. Oracle의 경우 부서와 사원의 테이블을 가지고 다음과 같은 SQL을 수행 시켰다고 가정 해보자.

img04.jpg

SELECT D.부서명, E.성명, E.주소
FROM 부서 D, 사원 E
WHERE D.부서코드 = E.부서코드

부서의 부서코드와 사원의 사원번호만 Primary Key로 선언이 되 있고 다른 조건은 아무것도 없으며, Nested Loop로 조인을 하는 경우 관계형 데이터베이스의 옵티마이져(Optimizer)가 어느쪽 테이블을 먼저 읽겠는가 부서 테이블인가 사원 테이블인가
첫 번째 부서 테이블을 먼저 읽는 경우를 보자. 부서 테이블의 정보를 한 건 읽고, 그 읽은 “부서코드”를 가지고 사원 테이블을 읽으러 간다. 사원 테이블의 “부서코드”에는 인덱스(Index)가 달려있지 않으므로 Full Table Scan을 한다. 이런 식으로 데이터를 읽어 간다면 부서 테이블의 50건에 대하여 50번 사원 테이블을 Full Table Scan 할 것이다.
두 번째 사원 테이블을 먼저 읽는 경우를 보자. 사원 테이블의 정보를 한 건 읽고, 그 읽은 사원의 “부서코드”를 가지고 부서 테이블을 읽으러 간다. 부서 테이블의 “부서코드”는 Primary Key로 Unique Index가 달려 있으므로 Unique Index Scan을 한 건 만 수행하고 정보를 제공할 것이다.
물론 데이터의 건수가 적은 경우는 Full Table Scan이 빠르겠지만 여기서는 Nested Loop조인의 원리를 설명하는 것이므로 실지로 테스트를 해보고 별 차이가 없네요 한다면 좀 곤란하다. 만약에 은행의 고객 테이블이나 계좌 테이블처럼 엄청난 양의 데이터를 가진 테이블을 대상으로 테스트를 해 본다면 확연히 속도의 차이가 나타날 것이다.
상식적으로 생각을 하더라도 첫 번째 방식은 Full Table Scan을 50번 반복하므로 문제가 많다. SQL문장 내에서 Join의 조건이 되는 컬럼 즉, 논리적으로 PK와 FK의 관계에 있는 컬럼(여기서 논리적이라는 얘기는 물리적으로 데이터베이스 내에 FK Constraint를 설정하지 않았다는 말이다)에 한 쪽에만 Index가 걸려 있는 경우를 “연결 고리 이상”이라고 한다. 이런 경우 옵티마이져는 무조건 Index가 걸려있지 않은 테이블을 먼저 읽는다. 하지만 다음과 같은 경우는 인덱스가 없다 하더라도 “연결 고리 이상”이라고 할 수 없다.

SELECT D.부서명, E.성명, E.주소
FROM 부서 D, 사원 E
WHERE D.부서코드 = E.부서코드
AND E.직급 = “부장” (직급 컬럼에 인덱스가 있슴)

SQL이 데이터를 접근하는 방식은 수학의 방정식을 푸는 방식과 같다. 상기의 SQL에서 사원의 “직급” 컬럼의 값이 상수 값 “부장”으로 제공되었기 때문에 옵티마이져는 사원 테이블을 먼저 읽고서 부서 테이블을 읽어 나간다. 만약에 다음과 같은 조건이 들어 온다면 이것은 성능에 영향을 미칠 것이다.

SELECT D.부서명, E.성명, E.주소
FROM 부서 D, 사원 E
WHERE D.부서코드 = E.부서코드
AND D.위치 = “부산” (위치 컬럼에 인덱스가 있슴)

이런 경우 부서 테이블을 먼저 읽고(왜냐하면 부서 테이블의 위치 컬럼이 상수 조건이므로) 위치가 “부산”부서코드를 가지고 사원 테이블을 읽으러 갈 것이다. 이 때 사원 테이블의 “부서코드”컬럼에 인덱스가 없다면 위치가 “부산”인 부서 개수 만큼 사원 테이블을 Full Table Scan할 것이다.
자 이제 부서 테이블의 “부서코드”와 사원 테이블의 “부서코드”의 Data Type이 틀린 경우를 보자.

img05.jpg

SELECT D.부서명, E.성명, E.주소
FROM 부서 D, 사원 E
WHERE D.부서코드 = E.부서코드
AND E.직급 = “부장” (직급 컬럼에 인덱스가 있슴)

테이블이 이렇게 설계되 있는 경우 즉, 부서코드의 Data Type이 위와 같은 경우 일단 사원의 직급 컬럼이 “부장”이라는 상수 조건으로 주어지므로 옵티마이져가 사원 테이블 먼저 읽을 것이다. 먼저 읽은 사원 테이블의 부서코드를 가지고 부서 테이블을 읽으러 가는데 사원의 부서코드는 Number 타입이고, 부서의 부서코드 Char 타입이다. 이런 경우 Oracle 데이터베이스에서는 내부적으로 Character 타입이 Number타입으로 변형된다. 이렇게 되면 부서 테이블의 부서코드 컬럼에 인덱스가 있다 하더라도 인덱스를 사용하지 못하고 Full Table Scan을 하게 된다. 이렇게 설계를 하는 사람이 어디 있냐고 반문을 할 수도 있다. 하지만 대형 프로젝트를 업무 영역별로 나누어 수행한다던가, 여러 개의 시스템을 Outsourcing하다 보면 외주 업체 엔지니어들의 수준이 다 틀리다 보니 이런 현상이 허다하게 나타난다.
중요한 것은 동일 성격의 데이터가 데이터 타입이 틀리면 위와 같이 인덱스 스캔을 못하고 Full Table Scan을 하여 수행 속도에 엄청난 폐를 끼칠 수도 있다는 것이다.

실체 무결성

데이터 구조의 세 번째 특성을 알아보자. 세 번째 특성은 각 행(Row)은 유일(Unique)하다라는 것인데 달리 표현하면 각 행(Row)은 하나 이상의 열(Column)의 값(Value)에 의하여 유일(Unique)하게 식별(Identify)될 수 있어야 한다. Primary Key에 대한 것을 규정하고 있다. 즉, Primary Key를 가지지 않으면 이는 테이블로 간주하지 않는다. 왜 이러한 특성을 가져야만 하는지를 실체 무결성(Entity Integrity)과 함께 설명을 하겠다.
실체 무결성(Entity Integrity)의 정의는 위에서 말한 것 처럼 주식별자(특정 행을 유일하게 인식하는 하나 이상의 열)는 Null 값을 포함하지 않는다. ”이다. Null이라는 값은 논리적으로는 “모르는 값”이라는 의미를 가진다. 우리가 현실 세계에서 어떤 하나의 개체를 인식하고자 할 때 그 개체를 인식할 수 있는 어떤 값이 주어져 있지 않더라도 그 개체를 인식할 수 있는 무엇 인가가 우리 머리 속에 떠오른다. 이 떠오르는 그 무엇이 그 개체의 형상(Image)이든, 어떤 숫자이든 상관 없이 그것은 Null(모르는 것)이 아니라는 것이다. 이런 이유에서 테이블에서 하나의 개체(인스턴스)를 인식하려면 Null(모르는 것)이어서는 안 되는 것이다. 그런데 내가 베토벤이라는 것에 대해서 얘기하고 있다고 가정을 해보자. 여기에서 내가 말하는 베토벤을 어떤 사람은 음악가 베토벤으로 인식할 것이고, 어떤 사람은 영화에 나오는 개 베토벤이라고 인식할 수 있다. 인간은 전후 문맥이나, 전체적인 상황을 판단하면서 이것이 음악가를 얘기하는 것인지 영화에 나온 개를 얘기하는 것인지를 판단한다. 하지만 컴퓨터는 주식별자(Primary Key)를 선정 할 때는 무엇인가 값(Not Null)을 가지며, 항상 유일(Unique)한 값을 가져야만 하나의 개체(인스턴스)를 정확하게 찾아낼 수 있는 것이다.
Primary Key가 유일하지 않으면 어떤 현상이 발생하는가 T1 테이블의 PK가 유일하게 구성되지 않았다고 가정을 해보자.

img06.jpg

관계 연사자를 이용하여 T1 테이블에서 T2 테이블과 T3 테이블을 만든 후 조인(Join)을 하여 T4라는 테이블을 만들면 원래는 두 개의 행(Row)만 있었는데 조인을 하면서 2 * 2 = 4 인 곱집합이 생겨 원래는 없었던 2 개의 행(Row)이 더 생겼다. 이런 경우 우리는 무결성(Integrity) 깨졌다고 한다.
Primary Key가 Null값을 가지면 어떤 현상이 발생하는가 T1 테이블의 PK가 Null값을 갖는다고 가정을 해보자.

img07.jpg

관계 연사자를 이용하여 T1 테이블에서 T2 테이블과 T3 테이블을 만든 후 조인(Join)을 하여 T4라는 테이블을 만들면 원래는 두 개의 행(Row)만 있었는데 조인을 하면서 NULL은 모르는 값이므로 조인 조건에 빠지게 되어 원래는 2개였던 행(Row)이 1개의 행(Row)만 생긴다(빨간색 행이 없어짐). 이런 경우도 우리는 무결성(Integrity) 깨졌다고 한다.
이러한 특성 외에 우리가 별도로 Primary Key의 선정 기준을 좀 더 살펴보면 Primary Key를 구성하는 각각의 컬럼수를 최소한의 집합(Minimal Set)으로 할 것과, 값의 수정이 없는 것, 업무적으로 활용도가 높은 것등을 들 수 있다. 최소한의 집합이 아니면 유일성(Unique)이 깨질 수 있으며, 값의 수정이 있는 것은 상기의 예에서 지점코드가 통폐합에 의해서 바뀐다던가 상품이 증가하여 자릿수가 늘어난다든가 하면, 자식 테이블에 미치는 영향이 많으므로 될 수 있으면 이런 컬럼들은 Primary Key 선정에서 제외한다.
다시 한번 강조하지만 관계형 데이터베이스 이전의 환경에서는 이렇게 값의 변화가 있는 컬럼 이라 해도 Key = Index이므로 즉 업무적으로 활용도가 크므로 이런 컬럼을 Primary Key로 잡았지만, 관계형 데이터베이스에서는 Primary Key는 값을 가지면서, 유일성만 보장해주면 되고, 업무적인 활용도는 인덱스(Index)를 설정하여 해결하면 된다. 관계형 데이터베이스에서 인덱스는 Key가 아닌 컬럼에도 언제든지 만들었다 제거했다 할 수 있으니 과거의 개념을 가지고 계신 분들은 이 점을 유의하기 바란다.
데이터 구조의 특성 중 나머지 세가지는 크게 고려를 안 해도 되는 사항으로 설명을 생략하겠다. 지금까지 데이터 구조의 특성 중에 세 가지와 데이터의 조작, 그리고 무결성 개념에서 실체 무결성과 영역무결성에 대해서 설명을 하였다. 이제 마지막으로 참조 무결성(Referential Integrity)에 대해서 알아보자.