💡제약 사항
제약사항(Constraint)은 해당 컬럼에 들어갈 데이터(값)에 대한 조건이다.
사용자는 컬럼에 엉뚱한 값이 들어가지 않고 올바른 값만 저장될 수 있도록 해야 한다.
만약 숫자형만 들어가야 하는 컬럼에 날짜형이나 문자형이 들어가는 등 데이터가 훼손이 되면 테이블의 무결성이 깨졌다는 표현을 쓴다.
하지만 올바른 데이터만 들어가는지 사람이 일일이 확인하기 어렵다. 그래서 사람이 오라클에게 규칙을 알려주고, 오라클이 이러한 규칙에 따라 데이터를 감시하고 관리한다. 이때 데이터의 정확성과 일관성을 유지하고 관리하기 위한 규칙을 제약사항이라고 한다.
- NOT NULL
- PRIMARY KEY (PK)
- FOREIGN KEY (FK)
- UNIQUE
- CHECK
- DEFAULT
NOT NULL
NOT NULL은 해당 컬럼이 필수값으로, 반드시 값을 가져야 한다는 것을 의미한다.
해당 컬럼에 값이 없으면 에러가 발생한다.
NULL의 사용
컬럼명 자료형(길이) NULL 제약사항
CREATE TABLE tblMemo
(
seq number(3) NULL, --메모번호
name varchar2(30) NULL, --작성자
memo varchar2(1000) NULL, --메모
regdate DATE NULL --작성날짜
);
tblMemo 테이블을 생성하며 컬럼명에 NULL을 추가하였다.
이때 NULL은 제약이 아니고 기본값이다.
이때 사용된 NULL은 값이 NULL을 허용한다는 뜻이다.
INSERT INTO tblMemo (seq, name, memo, regdate) values(1, 'Isaac', 'Memo입니다.', sysdate);
INSERT INTO tblMemo (seq, name, memo, regdate) values(2, 'Sopia', null, sysdate);
INSERT INTO tblMemo (seq, name, memo, regdate) values(3, null, null, null);
테이블에 값을 저장할 때, NULL 값이 들어간 것을 확인할 수 있다.
NOT NULL의 사용
DROP TABLE tblMemo;
CREATE TABLE tblMemo
(
seq NUMBER(3) NOT NULL, --메모번호(NN)
name varchar2(30) NULL, --작성자
memo varchar2(1000) NOT NULL, --메모(NN)
regdate DATE NULL --작성날짜
);
tblMemo 테이블을 지우고, NOT NULL이 적용되게 다시 작성해 보도록 하자.
INSERT INTO tblMemo (seq, name, memo, regdate) values(1, 'Isaac', 'Memo입니다.', sysdate);
-- ORA-01400: cannot insert NULL into ("HR"."TBLMEMO"."MEMO")
INSERT INTO tblMemo (seq, name, memo, regdate) values(2, 'Sopia', null, sysdate);
HR이 소유하고 있는 TBLMEMO 테이블의 MEMO에 데이터를 집어 넣어야 한다는 오류가 발생하였다.
-- ORA-01400: cannot insert NULL into ("HR"."TBLMEMO"."MEMO")
INSERT INTO tblMemo (seq, name, regdate) values(2, 'Sopia', sysdate);
컬럼은 생략할 수 있다. 컬럼을 생략할 경우 생략한 컬럼의 값도 생략이 되어야 한다.
생략한 컬럼에는 자동으로 null이 들어간다. 그래서 insert 때 특정 컬럼에 null을 넣는 방법으로 null을 넣는 명시적인 방법, 컬럼을 생략하는 방법 암시적인 방법을 사용할 수 있다.
하지만 이 경우에는 null을 허용하지 않았으므로 오류가 발생한다.
-- ORA-01400: cannot insert NULL into ("HR"."TBLMEMO"."MEMO")
INSERT INTO tblMemo (seq, name, regdate) values(2, '', sysdate);
오라클은 빈 문자도 null로 취급한다.
PRIMARY KEY (PK)
기본키는 테이블의 행을 구분하기 위한 제약 사항으로, 행을 식별하는 수많은 키(후보키)들 중 대표로 선정된 키를 의미한다.
모든 테이블은 반드시 1개의 기본키가 존재해야 한다.
기본키는 반드시 값을 가져야 하는 NOT NULL의 성질을 가지고 있다. 그리고 기본키는 중복값을 가질 수 없는데, 이러한 특성을 unique라고 한다.
PRIMARY KEY의 사용
CREATE TABLE tblMemo
(
seq number(3) PRIMARY KEY, --메모번호(PK)
name varchar2(30) NULL, --작성자
memo varchar2(1000) NOT NULL, --메모(NN)
regdate DATE NULL --작성날짜
);
INSERT INTO tblMemo (seq, name, memo, regdate) values(1, 'Isaac', 'Memo입니다.', sysdate);
중복값을 가지지 않으면서 생략되지 않는 필수값을 찾아 기본키로 지정한다.
작성자의 이름, 메모 내용, 작성날짜는 중복값을 가질 수 있다. 따라서 메모번호를 기본키로 지정하였다.
-- ORA-00001: unique constraint (HR.SYS_C007179) violated
INSERT INTO tblMemo (seq, name, memo, regdate) values(1, 'Isaac', 'Memo입니다.', sysdate);
이전에 테이블에 넣은 값과 똑같은 값을 넣으려고 하자 unique 제약을 위반했다는 오류가 발생한다.
새로 들어온 레코드의 number가 1로 동일하기 때문이다.
이렇게 기본키를 생성하면 자연스럽게 식별자가 역할을 할 수 있게 된다.
기본키가 하나씩은 관리되고 있어야 레코드를 구분할 수 있다.
-- ORA-01400: cannot insert NULL into ("HR"."TBLMEMO"."SEQ")
INSERT INTO tblMemo (seq, name, memo, regdate) values(null, 'Isaac', 'Memo입니다.', sysdate);
NOT NULL을 기입하지 않았지만, 기본키는 NOT NULL의 성질을 애초에 가지고 있기 때문에 생략을 하지 않는다.
FOREIGN KEY (FK)
외래키는 어떤 릴레이션에 소속된 속성 또는 속성 집합이 다른 릴레이션의 기본키가 되는 키를 의미한다.
REFERENCES
-- 직원(번호(PK), 직원명, 급여, 거주지)
CREATE TABLE tblStaff(
seq NUMBER PRIMARY KEY, --직원번호(PK)
name varchar2(30) NOT NULL, --직원명
salary NUMBER NOT NULL, --급여
address varchar2(300) NOT NULL --거주지
);
-- 프로젝트 정보
CREATE TABLE tblProject(
seq NUMBER PRIMARY KEY, --프로젝트 번호(PK)
project varchar2 (100) NOT NULL, --프로젝트명
staff_seq NUMBER NOT NULL REFERENCES tblStaff(seq) --담당 직원 번호
);
tblStaff 테이블을 tblProject 테이블이 의존하고 있다. 이를 보고 tblStaff를 부모 테이블, tblProject를 자식테이블이라고 한다.
참조하는 테이블과 참조당하는 테이블이 있으면 참조당하는 테이블부터 만들어야 한다.
지울 때는 tblProject 테이블부터 지우고, tblStaff 테이블을 지울 수 있다.
테이블을 만들 때에는 부모부터 만들고 자식을 만든다. 테이블을 지울 때에는 자식을 지우고 부모를 지운다.
Foreign Key의 사용
Foreign Key의 사용 예시로는 위 글의 문제 발생 > 신입 사원 입사 부분을 참고하도록 한다.
UNIQUE
UNIQUE는 유일하기 때문에 레코드 간에 중복값을 가질 수 없다.
NULL을 가질 수 있기 때문에 식별자가 될 수 없다는 특징이 있다.
초등학교 교실
학생(번호(PK), 이름(NN), 직책(UQ))
UNIQUE의 예시로 학생은 기본키, 이름은 NOT NULL, 직책은 UNIQUE를 부여할 수 있다.
직책에 UNIQUE를 부여한 이유는 직책을 가질 수도 가지지 않을 수도 있지만, 그 직책이 반장, 부반장이 1명으로 유일해야 하기 때문이다.
UNIQUE의 사용
CREATE TABLE tblMemo
(
seq number(3) PRIMARY KEY, --메모번호(PK)
name varchar2(30) UNIQUE, --작성자(UQ)
memo varchar2(1000) NOT NULL, --메모(NN)
regdate DATE --작성날짜
);
INSERT INTO tblMemo (seq, name, memo, regdate) values(1, 'Isaac', 'Memo입니다.', sysdate);
INSERT INTO tblMemo (seq, name, memo, regdate) values(2, 'Sopia', 'Memo입니다.', sysdate);
INSERT INTO tblMemo (seq, name, memo, regdate) values(3, 'Paul', 'Memo입니다.', sysdate);
INSERT INTO tblMemo (seq, name, memo, regdate) values(4, null, 'Memo입니다.', sysdate);
INSERT INTO tblMemo (seq, name, memo, regdate) values(5, null, 'Memo입니다.', sysdate);
CHECK
CHECK는 사용자 정의형으로 작성한다.
WHERE절의 조건을 컬럼의 제약 사항으로서 적용한다고 이해하면 된다.
CHECK의 사용
CREATE TABLE tblMemo
(
seq number(3) PRIMARY KEY, --메모번호(PK)
name varchar2(30), --작성자
memo varchar2(1000), --메모
regdate DATE, --작성날짜
--중요도(1(중요), 2(보통), 3(안중요))
--priority NUMBER(1) CHECK (priority >= 1 AND priority <= 3)
priority NUMBER(1) CHECK (priority BETWWEEN 1 AND 3),
--카테고리(할일, 공부, 약속)
--category varchar2(30) CHECK (category = '할일' OR category = '공부' OR category = '약속')
category varchar2(30) CHECK (category IN ('할일', '공부', '약속'))
);
INSERT INTO tblMemo (seq, name, memo, regdate, priority, category) values(1, 'Isaac', 'Memo입니다.', sysdate, 1, '할일');
--ORA-02290: check constraint (HR.SYS_C007195) violated
INSERT INTO tblMemo (seq, name, memo, regdate, priority, category) values(2, 'Isaac', 'Memo입니다.', sysdate, 5, '할일');
--ORA-02290: check constraint (HR.SYS_C007196) violated
INSERT INTO tblMemo (seq, name, memo, regdate, priority, category) values(3, 'Sopia', 'Memo입니다.', sysdate, 3, '가족');
tblMemo 테이블에서 중요도는 1, 2, 3을 입력해야 하지만, 이외의 숫자를 입력할 가능성이 있다.
이럴 때 사용하는 게 CHECK 제약이다.
CHECK 제약은 WHERE절에서 사용할 때와 같이 조건으로 작성한다.
DEFAULT
DEFUALT는 insert/update 작업 시 컬럼에 값을 안 넣으면 null 대신 미리 설정한 기본값을 대입한다.
제약사항은 에러를 내는 걸 목적으로 하지만, DEFUALT는 에러를 내지 않는다.
DEFAULT의 사용
CREATE TABLE tblMemo
(
seq number(3) PRIMARY KEY, --메모번호(PK)
name varchar2(30) DEFAULT '익명', --작성자
memo varchar2(1000), --메모
regdate DATE DEFAULT sysdate --작성날짜
);
INSERT INTO tblMemo (seq, name, memo, regdate) values(1, 'Isaac', 'Memo입니다.', sysdate);
INSERT INTO tblMemo (seq, name, memo, regdate) values(2, null, 'Memo입니다.', null);
null을 명시하자 null이 그대로 들어갔다.
INSERT INTO tblMemo (seq, memo) values(3, 'Memo입니다.');
암시적인 null을 사용하였다.
사용자의 의도를 우선하였기 때문에 null을 넣겠다고 명시할 때에는 null을 넣고, 암시적으로 null을 넣었을 때에는 defualt 값을 우선하여 넣는다.
INSERT INTO tblMemo (seq, name, memo, regdate) values(4, DEFAULT, 'Memo입니다.', default);
명시적으로 default 상수를 이용하여 default 값을 넣을 수 있다.
💡제약 사항을 만드는 방법
컬럼 수준에서 정의
컬럼을 선언할 때 제약 사항도 같이 선언하는 방법이다.
CREATE TABLE tblMemo
(
seq number(3) PRIMARY KEY
);
seq number(3) PRIMARY KEY를 컬럼 수준에서 정의했다고 한다.
테이블 수준에서 정의
CREATE TABLE tblMemo
(
seq number(3), --CONSTRAINT tblMemo_seq_pk PRIMARY KEY
name varchar2(30), --CONSTRAINT tblMemo_name_uq UNIQUE
memo varchar2(1000), --CONSTRAINT tblMemo_memo_ck CHECK (LENGTH(memo) >= 10)
regdate DATE NOT NULL,
--테이블 수준에서 제약 사항 정의
CONSTRAINT tblMemo_seq_pk PRIMARY KEY(seq),
CONSTRAINT tblMemo_name_uq UNIQUE(name),
CONSTRAINT tblMemo_memo_ck CHECK (LENGTH(memo) >= 10)
);
컬럼 아래에 테이블 수준에서 정의를 하는 이유는 가독성 때문이다.
PRIMARY KEY로 생략하여 작성하긴 했지만, CONSTRAINT tblMemo_seq_pk PRIMARY KEY가 기본키의 전체 구문이다.
_seq_pk는 제약 사항의 식별자이며, 그 뒤에 제약 사항 이름을 붙인다. 만약 _seq_op를 생략할 경우 자동으로 이름인 HR.SYS_C007195가 붙는다.
제약 사항의 식별자는 반드시 작성하도록 한다!