본문 바로가기
Infra & Security Eng/Database Engineering

[Oracle] 제약 조건(UK, CHECK, NOT NULL) 완벽 정리와 실습

by 엔지니어 E 2026. 2. 10.
반응형

1. UK 개념 (Unique Key, 고유 키)

해당 컬럼에 중복된 값이 들어올 수 없도록 보장함 (예시. 이메일, 주빈번호 등)

 

2. NOT NULL 개념 (필수 입력 제약)

해당 컬럼에 NULL(빈 값)이 들어오는 것을 방지함. 즉, 데이터를 입력할 때 이 컬럼은 무조건 값을 채워야 함을 의미함

 

3. 상세 비교 테이블

구분 NOT NULL UNIQUE (UK)
목적 빈 값 방지 (필수값 설정) 중복 값 방지 (유일성 보장)
NULL 허용 불가능 가능
인덱스 생성 생성 안 됨 자동으로 고유 인덱스 생성
정의 위치 컬럼 레벨만 가능 컬럼 & 테이블 레벨 모두 가능

 

4. UK(Unique Key), NOT NULL 설정과 예제

모든 칸(컬럼)을 다 만든 뒤에, 맨 밑에서 한꺼번에 규칙을 정하는 방법
CREATE TABLE 테이블 (

.....
CONSTRAINT 제약_조건 UNIQUE (컬럼));

예제
CREATE TABLE member (
id  VARCHAR2(10) PRIMARY KEY, -- 이 칸의 이름은 ID 라고 부르겠다, 최대 10글자 까지 문자를 넣고, PRIMARY KEY(제약 조건) 은 비어있으면 안 되고(NOT NULL), 중복되어도 안 된다(UNIQUE)

ID
----------
USER01

    name  VARCHAR2(20) CONSTRAINT mem_name_nn NOT NULL, ---> 이름은 비어있으면 안 됨(컬럼 레벨)
    email VARCHAR2(50) CONSTRAINT mem_email_uk UNIQUE, ---> 메일은 중복되면 안됨 (컬럼 레벨)
    phone VARCHAR2(15),
    CONSTRAINT mem_phone_uk UNIQUE (phone) ---> 전화번호 중복 방지(테이블 레벨 방식)
);

* 컬럼 레벨: 컬럼을 만드는 그 줄에 제약 조건을 바로 적는 방식 
* 테이블 레벨: 모든 컬럼을 다 나열한 뒤, 맨 마지막 줄에 제약 조건을 따로 모아서 적는 방식

칸(컬럼)을 만들면서 그 자리에서 즉시 규칙을 정하는 방법

CREATE TABLE 테이블 (
컬럼 데이터_타입 CONSTRAINT 제약_조건 NOT NULL,
......;

예제
CREATE TABLE member (
    userid VARCHAR2(20) CONSTRAINT mem_id_nn NOT NULL,   -- 아이디 (필수 입력)
    name   VARCHAR2(30) CONSTRAINT mem_nm_nn NOT NULL  -- 이름 (필수 입력)
    age    NUMBER                                       -- 나이 (선택 사항, 빈값 허용)
);

 

5. CHECK 개념

데이터가 입력될 때, 그 값 자체가 미리 정해둔 조건에 맞는지 검사하는 제약조건
단순히 중복이나 빈 값을 막는 것을 넘어, 데이터의 내용이 논리적으로 올바른지 확인

 

6. CHECK 설정과 예제

CREATE TABLE 테이블 (
.....
CONSTRAINT 제약_조건 CHECK (조건));

예제
CREATE TABLE goods ( ---> goods 라는 이름의 데이터 저장소를 생성
    gno    VARCHAR2(8), ---> 제품 번호를 저장, 최대 8글자의 문자(숫자 포함)를 허용
    gname  VARCHAR2(50), ---> 제품 이름을 저장, 최대 50글자의 문자를 허용
    pri    NUMBER(10, 2), ---> 제품 가격을 저장, 소수점 초함 총 10자리(소수점 2자리 포함) 숫자를 허용
    -- [CHECK 제약조건]
    -- 이름: goods_chk_pri
    -- 내용: pri(가격) 컬럼에 들어오는 숫자는 반드시 0보다 커야 함을 검사함
    CONSTRAINT goods_chk_pri CHECK (pri > 0) 
);


 

UK, CHECK, NOT NULL을 적용한 테이블 생성

컬럼명 eno(사번) ename(이름) gno(주민번호) sex(성별)
PK/UK/NOT NULL PK NOT NULL UK  
참조 테이블        
참조 컬럼        
CHECK     LENGTH(13) (여, 남)
데이터 타입     VARCHAR2 VARCHAR2
길이 4 50 13 4
CREATE TABLE emp3 (                                     -- emp3라는 이름의 테이블(저장소)을 생성
    eno    VARCHAR2(4),                                 -- 사번(eno)은 최대 4글자까지 문자로 저장
    ename  VARCHAR2(50) CONSTRAINT emp3_ename_nu NOT NULL, -- 이름(ename)은 최대 50글자이며, 비워둘 수 없음 (NOT NULL).
    gno    VARCHAR2(13),                                -- 주민번호(gno)는 13글자 문자로 저장함
    sex    VARCHAR2(4),                                 -- 성별(sex)은 최대 4바이트(한글 1~2자)까지 문자로 저장함
    CONSTRAINT emp3_eno_pk PRIMARY KEY (eno),           -- 사번(eno)을 중복이나 빈값 없이 유일한 열쇠로 지정함
    CONSTRAINT emp3_gno_uk UNIQUE (gno),                -- 주민번호(gno)는 다른 사람과 겹치지 않게 유일해야 함
    CONSTRAINT emp3_gno_ch CHECK (LENGTH(gno) = 13),    -- 주민번호(gno)는 반드시 정확히 13글자여야만 저장됨
* CHECK(LENGTH(gno)=13: gno에 데이터를 넣을때마다 매번 글자 수를 확인 해서 13글자가 아니면 입구컷 시켜라 라는 뜻
    CONSTRAINT emp3_sex_ch CHECK (sex IN ('여', '남'))   -- 성별(sex)은 '여' 또는 '남'이라는 글자만 입력 가능함
* CHECK (sex IN ('여', '남'): sex 컬럼에 들어올 값이 '여' 아니면 '남' 둘 중 하나일때만 입장을 허용해라 라는 뜻
);

emp3_eno_pk 풀이
emp3: 테이블 이름
eno: 컬럼 이름 
pk: 제약 조건의 종류(Primary Key) 

주민번호: 주 식별자로 가능하지만 너무 길어서 사용하지 않음

* 주 식별자는 길이가 짧아야 함

 

실습하기

아래 도표를 보고 테이블을 생성한다. 
- 테이블 명에 한글을 허용 한다

논리프로세스 
1단계 - 빌려줄 데이터(기준)를 먼저 만든다 
제품 테이블과 판매전표 테이블을 먼저 만들어야 한다 
이유: 전표 상세 테이블은 어떤 물건(제품)을 어떤 영수증(판매전표)에 담을지 기록하는 곳이다. 물건 정보와 영수증 번호가 세상에 먼저 존재해야 그것을 가져다 쓸 수 있다 

2단계 - 빌려 쓰는 데이터(상세)를 나중에 만든다 
전표 상세 테이블을 마지막에 만든다

이유: 이 테이블은 FOREIGN KEY(외래키) 를 통해 제품의 번호와 판매전표의 번호를 빌려와서 사용한다. 만약 기준이 되는 테이블들이 없다면, 컴퓨터는 어디서 번호를 가져오는거야라며 에러를 가져오게 된다 

3단계 - 삭제할 때는 만든 순서의 반대로 한다
삭제할 때는 전표상세부터 지운다 
이유: 엄마(기준 테이블) 를 지우려는데 자식(상세 테이블이) 이 엄마 다리를 붙잡고(참조) 있으면 지울 수 없기 때문이다. 그래서 붙잡고 있는 자식부터 지우고 엄마(부모)를 지웅는 순서가 안전하다
실습하기

SET LINESIZE 150; -- 화면 가로 길이를 150자로 넓혀서 표가 깨지지 않게 함

SET PAGESIZE 50;  -- 세로 50줄마다 제목이 나오게 설정함

1. 기존 테이블 삭제 (자식 테이블인 '전표상세'부터 지워야 오류가 안 남)
DROP TABLE "전표상세" CASCADE CONSTRAINTS; -- 판매전표와 제품을 참고하는 상세 테이블 먼저 삭제
DROP TABLE "판매전표" CASCADE CONSTRAINTS; -- 전표의 기본 정보를 담은 테이블 삭제
DROP TABLE "제품" CASCADE CONSTRAINTS;     -- 제품 목록이 담긴 테이블 삭제

2. [제품] 테이블: 팔 물건들의 정보를 모아둔 곳
CREATE TABLE "제품" (
    "제품번호" VARCHAR2(12), -- 제품의 고유 번호 (최대 12자)
    "제품명"   VARCHAR2(100), -- 제품의 이름 (최대 100자)
    "제품단가" NUMBER,        -- 제품 1개의 가격
    CONSTRAINT "제품_제품번호_PK" PRIMARY KEY ("제품번호"), -- 제품번호는 중복 안 되고 비어있으면 안 됨
    CONSTRAINT "제품_제품명_UK" UNIQUE ("제품명"),         -- 제품 이름도 서로 중복될 수 없음
    CONSTRAINT "제품_제품단가_CK" CHECK ("제품단가" > 0)    -- 제품 가격은 반드시 0보다 커야 함
);

3. [판매전표] 테이블: 누가 언제 샀는지 큰 정보를 담는 곳
CREATE TABLE "판매전표" (
    "전표번호" VARCHAR2(12), -- 영수증 번호 (최대 12자)
    "판매일자" DATE         CONSTRAINT "판매전표_판매일자_NN" NOT NULL, -- 언제 샀는지 반드시 적어야 함
    "고객명"   VARCHAR2(50) CONSTRAINT "판매전표_고객명_NN" NOT NULL,   -- 산 사람 이름을 반드시 적어야 함
    "총액"     NUMBER       CONSTRAINT "판매전표_총액_NN" NOT NULL,     -- 전체 금액을 반드시 적어야 함
    CONSTRAINT "판매전표_전표번호_PK" PRIMARY KEY ("전표번호"), -- 전표번호는 중복 안 되고 비어있으면 안 됨
    CONSTRAINT "판매전표_총액_CK" CHECK ("총액" > 0)           -- 전체 금액은 반드시 0보다 커야 함
* 실수나 오류로 인해 가격이 0원(공짜)이나 마이너스(마이너스 금액)으로 입력되는 상황을 봉쇄하여 데이터의 정확성을 지키기 위해

);

4. [전표상세] 테이블: 한 장의 영수증 안에 어떤 물건들을 몇 개 샀는지 구체적으로 적는 곳
CREATE TABLE "전표상세" (
    "전표번호" VARCHAR2(12), -- 어떤 영수증에 속하는지 (판매전표 번호)
    "제품번호" VARCHAR2(12), -- 어떤 물건을 샀는지 (제품 번호)
    "수량"     NUMBER CONSTRAINT "전표상세_수량_NN" NOT NULL-- 몇 개 샀는지 반드시 적어야 함
    "단가"     NUMBER CONSTRAINT "전표상세_단가_NN" NOT NULL, -- 당시 가격을 반드시 적어야 함
    "금액"     NUMBER CONSTRAINT "전표상세_금액_NN" NOT NULL, -- (수량x단가) 금액을 반드시 적어야 함
    -- 제약 조건
    CONSTRAINT "전표상세_PK" PRIMARY KEY ("전표번호", "제품번호"), -- 한 영수증 안에서 같은 제품은 한 줄만 기록함
    CONSTRAINT "전표상세_전표번호_FK" FOREIGN KEY ("전표번호") REFERENCES "판매전표" ("전표번호"), -- 실제 있는 영수증 번호만 써야 함
    CONSTRAINT "전표상세_제품번호_FK" FOREIGN KEY ("제품번호") REFERENCES "제품" ("제품번호"),     -- 실제 있는 제품 번호만 써야 함
    CONSTRAINT "전표상세_금액_CK" CHECK ("금액" > 0) -- 계산된 금액은 반드시 0보다 커야 함
);

-- 테이블이 잘 만들어졌는지 설계도 확인
DESC "제품";
DESC "판매전표";
DESC "전표상세";