지금까지 다룬 서비스 중에 가장 큰 서비스는 취업을 위해 개인적으로 진행한 일정 관리 프로그램이었다. 그래서 데이터의 개수도 작았고 테이블의 컬럼도 상대적으로 많이 약소했다. 현재 서버 개발자로 업무를 하고 있진 않지만 데이터가 잘 이동할 수 있도록 개발을 하고 있기 때문에 데이터와 관련 있는 업무를 진행하고 있다. 그러면서 많은 테이블의 컬럼을 보면서 필자가 이제까지 설계한 테이블과 다른 점을 한눈에 파악할 수 있었는데 그중에서 눈에 띈 것이 바로 Primary Key(기본키)였고 이번 포스팅에서 기본키에 대해 알아보려고 한다.
기본키란?
필자가 쓰고자 하는 내용에 앞서 먼저 기본키가 무엇인지에 대해 아는 것이 먼저인 것 같아 기본키에 대해 설명하면 이름에서 말하듯 기본이 되는 키다. 정말 성의 없는 설명이다.
다른 키와 차이점을 통해 기본키에 대해 알아보면 먼저 DB에서 사용하는 키에는 아래처럼 대체키, 후보 키, 슈퍼 키 등이 있다.
기본키는 테이블에 존재하는 수많은 데이터들을 구별할 수 있는 유일한 기준이 되는 속성이다.
개인 정보에는 자기 자신을 특정할 수 있는 것(예를 들면, 주민등록번호)과 타인과 같은 값을 가지는 것(예를 들면, 이름 키 체중 생년월일 등)이 있다.
그 속성 중에 이름으로 구분하게 되면 하나의 이름에 부과한 벌금이 그 사람뿐만 아니라 동명이인에게도 미치게 된다. 그래서 이름처럼 중복될 수 있는 값이 아닌 주민등록번호와 같은 유니크한 속성으로 사람을 구별하는데 데이터를 구별하는 것도 마찬가지다.
테이블에는 수많은 데이터가 존재하고 그 데이터들은 분명히 중복된 값을 가질 수밖에 없다. 그래서 기본키를 통해 데이터를 구분하는 것이다.
그래서 기본키는 항상 값을 가져야 하며 그 값은 고유한 값이어야 하고 테이블 당 하나만 만들 수 있다.
그렇다면 후보 키는 무엇일까?
우리가 게임을 할 때 가장 고민하는 순간이 언제일까? 필자는 캐릭터의 닉네임을 정할 때 가장 고민을 한다.
그 닉네임은 게임에서 나를 대신하기 때문에 재밌거나 혹은 독특한 것을 하게 된다. 하지만 닉네임은 사람의 이름같이 중복된 값을 허용하지 않는다. 즉, 게임이라고 범위를 한정 짓게 되면 닉네임은 기본키라고 할 수 있게 된다.
하지만 범위를 좀 더 넓혀서 게임 관리자 입장에서 보면 해당 닉네임으로 유저를 특정할 수 있지만 추가적으로 가입할 때 입력한 주민등록번호로 유저를 특정할 수 있게 된다.(한 유저당 생성할 수 있는 캐릭터는 하나라고 가정한다.)
관리자 입장에서 게임 닉네임으로 기본키를 설정해도 되지만 주민등록번호로 기본키를 설정해도 무방하다. 그래서 둘 다 기본키가 될 수 있는 '후보 키'가 되는 것이다.
후보 키는 테이블에서 각 데이터를 구별할 수 있는 하나 혹은 그 이상의 컬럼의 집합을 의미한다. 그래서 후보 키 중에 기본키가 되는 컬럼을 제외한 다른 키들은 대체키가 되는 것이다. 즉, 주민등록번호를 기본키로 설정하면 닉네임이 대체키가 되는 것이다.
슈퍼 키는 무엇일까??
위 그림을 보면 슈퍼 키는 가장 바깥에서 모든 키를 아우르고 있다.
위에서 설명한 것처럼 개인정보 중에 주민등록번호로 개인을 구별할 수 있지만 (주민등록번호 + 이름) 혹은 (주민등록번호 + 성별)처럼 다양한 조합으로 개인을 구별할 수 있다.
근데 여기서 의문이 생긴다. 주민등록번호로도 구별이 가능한데 굳이 이름이나 성별을 더 넣어서 조합할 필요가 있을까?
유일성 : 하나의 키 값으로 하나의 데이터를 식별할 수 있어야 한다.
최소성 : 꼭 필요한 최소의 속성으로 구성되어야 한다.
슈퍼 키는 조합을 하기 때문에 유일성은 충족하지만 최소성을 충족하지 못한다. 그래서 슈퍼 키는 가장 바깥에서 모든 키를 포함하는 것이다.
그런데 최근에 업무를 하면서 기본키가 여러 개인 테이블을 보게 되었다. 분명히 위에서 기본키는 테이블마다 하나씩만 지정할 수 있다고 했는데 이게 어떻게 된 것일까??
복합 키란?
복합 키는 두 개 이상의 컬럼을 Key로 지정하는 것을 말한다. 앞서 언급했듯이 테이블당 하나의 기본키만 생성할 수 있다. 하지만 그 말이 곧 '하나의 컬럼만 기본키로 지정해라'라는 뜻으로 이어지진 않는다. 두 개 이상의 컬럼을 하나의 기본키로 생성해도 된다는 말이다.
수강신청 테이블을 만든다고 가정한다면 기본적으로 학번, 강의 번호, 강의 이름 등이 필요할 것이다.
CREATE TABLE CLASS(
STUDENT_NAME NUMBER(10) PRIMARY KEY,
CLASS_CODE NUMBER(10) PRIMARY KEY,
CLASS_NAME VARCHAR2(30 CHAR),
GENDAR VARCHAR2(1 CHAR) DEFAULT 'M'
);
학번을 기본 키로 설정할 수 있지만 이렇게 할 경우 학번의 값은 중복이 되면 안 되기 때문에 학생들은 하나의 강의만 들어야 한다. 그래서 (학번 + 강의 번호)로 기본키를 지정하면 학번이나 강의 번호가 중복되어도 기본키를 위반한 것이 아니므로 문제 될 것이 없다.
위 CREATE문을 실행하면 아래와 같은 오류와 마주하게 된다.
'기본키가 여러 개 설정되었으니 여분의 기본키는 제거하라'는 오류이다. 이럴 경우 문법을 변경해서 생성하면 우리가 원하는 목적을 이룰 수 있다.
CREATE TABLE CLASS(
STUDENT_NAME NUMBER(10),
CLASS_CODE NUMBER(10),
CLASS_NAME VARCHAR2(30 CHAR),
GENDAR VARCHAR2(1 CHAR) DEFAULT 'M',
CONSTRAINT CLASS_PK PRIMARY KEY(STUDENT_NAME, CLASS_CODE)
);
테이블이 정상적으로 생성되었으니 데이터를 입력하여 STUDENT_NAME 혹은 CLASS_CODE가 중복되어도 문제없이 데이터가 삽입되는지 확인해보자.
INSERT INTO CLASS VALUES(20220101, 0001, 'JAVA의 정석', 'M');
1 행 이(가) 삽입되었습니다.
INSERT INTO CLASS VALUES(20220102, 0001, 'Python의 정석', 'F');
1 행 이(가) 삽입되었습니다.
STUDENT_NAME은 다르게 하고 CLASS_CODE는 동일하게 했지만 문제없이 삽입이 되었다.
INSERT INTO CLASS VALUES(20220101, 0002, 'Php의 정석', 'F');
1 행 이(가) 삽입되었습니다.
기존에 있는 STUDENT_NAME과 동일한 데이터를 삽입해도 에러가 발생하지 않는다. 이때 우리는 STUDENT_NAME과 CLASS_CODE 둘 다 동일한 데이터를 삽입하면 어떤 일이 발생하는지 보면 된다.
INSERT INTO CLASS VALUES(20220102, 0001, 'DBMS의 모든 것', 'M');
위에서 설정한 기본키를 위배했다는 에러와 함께 데이터가 삽입되지 않았다.
이렇게 복합 키를 사용하여 기본키를 설정하면 기본키를 구성하는 하나의 키가 중복되어도 데이터는 문제없이 삽입된다는 것을 확인할 수 있었다.
슈퍼 키와 복합 키의 차이는?
슈퍼 키와 복합 키는 차이가 없어 보이지만 차이가 있다면 '구성하고 있는 키가 어떤 키냐'다.
슈퍼 키는 복합 키와 달리 구성되는 키가 후보 키에 들어가지 않는다. (학번 + 이름) 구조를 보면 학번은 기본키로 후보 키의 범주에 속하지만 이름은 후보 키에 속하지 않는 중복될 수 있는 컬럼이다.
복합 키는 구성되는 키가 모두 후보 키에 속한다. (학번 + 강의 코드) 구조를 보면 학번으로 학생을 구별할 수 있고, 강의 코드로 강의를 구별할 수 있다. 그래서 중복된 값을 허용하기 위해 2개의 후보 키를 복합키로 하여 기본키를 생성한 것이다.
정리
슈퍼 키는 키를 구성하는 컬럼 중 없어도 되는 컬럼이 포함되어있음.
복합 키는 키를 구성하는 컬럼 중 없으면 안 되는 컬럼이 포함되어있음.
REFERENCE
'DataBase' 카테고리의 다른 글
[Oracle] ORA-01008 : not all variables bound (0) | 2023.02.13 |
---|---|
[MariaDB] Truncated incorrect decimal value '' (0) | 2023.02.02 |
[DataBase] 정규화에 대해 알아보자! (0) | 2022.07.21 |
[DataBase] RDBMS VS NoSQL (0) | 2022.07.08 |
[DataBase] Join을 왜 써야 하지?(+ Join 종류) (0) | 2022.07.07 |